##// END OF EJS Templates
extdiff: document that it copies modified files back to working directory...
Kyle Lippincott -
r37226:2208149c default
parent child Browse files
Show More
@@ -1,422 +1,427
1 # extdiff.py - external diff program support for mercurial
1 # extdiff.py - external diff program support for mercurial
2 #
2 #
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.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 '''command to allow external programs to compare revisions
8 '''command to allow external programs to compare revisions
9
9
10 The extdiff Mercurial extension allows you to use external programs
10 The extdiff Mercurial extension allows you to use external programs
11 to compare revisions, or revision with working directory. The external
11 to compare revisions, or revision with working directory. The external
12 diff programs are called with a configurable set of options and two
12 diff programs are called with a configurable set of options and two
13 non-option arguments: paths to directories containing snapshots of
13 non-option arguments: paths to directories containing snapshots of
14 files to compare.
14 files to compare.
15
15
16 If there is more than one file being compared and the "child" revision
17 is the working directory, any modifications made in the external diff
18 program will be copied back to the working directory from the temporary
19 directory.
20
16 The extdiff extension also allows you to configure new diff commands, so
21 The extdiff extension also allows you to configure new diff commands, so
17 you do not need to type :hg:`extdiff -p kdiff3` always. ::
22 you do not need to type :hg:`extdiff -p kdiff3` always. ::
18
23
19 [extdiff]
24 [extdiff]
20 # add new command that runs GNU diff(1) in 'context diff' mode
25 # add new command that runs GNU diff(1) in 'context diff' mode
21 cdiff = gdiff -Nprc5
26 cdiff = gdiff -Nprc5
22 ## or the old way:
27 ## or the old way:
23 #cmd.cdiff = gdiff
28 #cmd.cdiff = gdiff
24 #opts.cdiff = -Nprc5
29 #opts.cdiff = -Nprc5
25
30
26 # add new command called meld, runs meld (no need to name twice). If
31 # add new command called meld, runs meld (no need to name twice). If
27 # the meld executable is not available, the meld tool in [merge-tools]
32 # the meld executable is not available, the meld tool in [merge-tools]
28 # will be used, if available
33 # will be used, if available
29 meld =
34 meld =
30
35
31 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
36 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
32 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
37 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
33 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
38 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
34 # your .vimrc
39 # your .vimrc
35 vimdiff = gvim -f "+next" \\
40 vimdiff = gvim -f "+next" \\
36 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
41 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
37
42
38 Tool arguments can include variables that are expanded at runtime::
43 Tool arguments can include variables that are expanded at runtime::
39
44
40 $parent1, $plabel1 - filename, descriptive label of first parent
45 $parent1, $plabel1 - filename, descriptive label of first parent
41 $child, $clabel - filename, descriptive label of child revision
46 $child, $clabel - filename, descriptive label of child revision
42 $parent2, $plabel2 - filename, descriptive label of second parent
47 $parent2, $plabel2 - filename, descriptive label of second parent
43 $root - repository root
48 $root - repository root
44 $parent is an alias for $parent1.
49 $parent is an alias for $parent1.
45
50
46 The extdiff extension will look in your [diff-tools] and [merge-tools]
51 The extdiff extension will look in your [diff-tools] and [merge-tools]
47 sections for diff tool arguments, when none are specified in [extdiff].
52 sections for diff tool arguments, when none are specified in [extdiff].
48
53
49 ::
54 ::
50
55
51 [extdiff]
56 [extdiff]
52 kdiff3 =
57 kdiff3 =
53
58
54 [diff-tools]
59 [diff-tools]
55 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
60 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
56
61
57 You can use -I/-X and list of file or directory names like normal
62 You can use -I/-X and list of file or directory names like normal
58 :hg:`diff` command. The extdiff extension makes snapshots of only
63 :hg:`diff` command. The extdiff extension makes snapshots of only
59 needed files, so running the external diff program will actually be
64 needed files, so running the external diff program will actually be
60 pretty fast (at least faster than having to compare the entire tree).
65 pretty fast (at least faster than having to compare the entire tree).
61 '''
66 '''
62
67
63 from __future__ import absolute_import
68 from __future__ import absolute_import
64
69
65 import os
70 import os
66 import re
71 import re
67 import shutil
72 import shutil
68 import stat
73 import stat
69 import tempfile
74 import tempfile
70 from mercurial.i18n import _
75 from mercurial.i18n import _
71 from mercurial.node import (
76 from mercurial.node import (
72 nullid,
77 nullid,
73 short,
78 short,
74 )
79 )
75 from mercurial import (
80 from mercurial import (
76 archival,
81 archival,
77 cmdutil,
82 cmdutil,
78 error,
83 error,
79 filemerge,
84 filemerge,
80 pycompat,
85 pycompat,
81 registrar,
86 registrar,
82 scmutil,
87 scmutil,
83 util,
88 util,
84 )
89 )
85 from mercurial.utils import (
90 from mercurial.utils import (
86 procutil,
91 procutil,
87 stringutil,
92 stringutil,
88 )
93 )
89
94
90 cmdtable = {}
95 cmdtable = {}
91 command = registrar.command(cmdtable)
96 command = registrar.command(cmdtable)
92
97
93 configtable = {}
98 configtable = {}
94 configitem = registrar.configitem(configtable)
99 configitem = registrar.configitem(configtable)
95
100
96 configitem('extdiff', br'opts\..*',
101 configitem('extdiff', br'opts\..*',
97 default='',
102 default='',
98 generic=True,
103 generic=True,
99 )
104 )
100
105
101 configitem('diff-tools', br'.*\.diffargs$',
106 configitem('diff-tools', br'.*\.diffargs$',
102 default=None,
107 default=None,
103 generic=True,
108 generic=True,
104 )
109 )
105
110
106 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
111 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
107 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
112 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
108 # be specifying the version(s) of Mercurial they are tested with, or
113 # be specifying the version(s) of Mercurial they are tested with, or
109 # leave the attribute unspecified.
114 # leave the attribute unspecified.
110 testedwith = 'ships-with-hg-core'
115 testedwith = 'ships-with-hg-core'
111
116
112 def snapshot(ui, repo, files, node, tmproot, listsubrepos):
117 def snapshot(ui, repo, files, node, tmproot, listsubrepos):
113 '''snapshot files as of some revision
118 '''snapshot files as of some revision
114 if not using snapshot, -I/-X does not work and recursive diff
119 if not using snapshot, -I/-X does not work and recursive diff
115 in tools like kdiff3 and meld displays too many files.'''
120 in tools like kdiff3 and meld displays too many files.'''
116 dirname = os.path.basename(repo.root)
121 dirname = os.path.basename(repo.root)
117 if dirname == "":
122 if dirname == "":
118 dirname = "root"
123 dirname = "root"
119 if node is not None:
124 if node is not None:
120 dirname = '%s.%s' % (dirname, short(node))
125 dirname = '%s.%s' % (dirname, short(node))
121 base = os.path.join(tmproot, dirname)
126 base = os.path.join(tmproot, dirname)
122 os.mkdir(base)
127 os.mkdir(base)
123 fnsandstat = []
128 fnsandstat = []
124
129
125 if node is not None:
130 if node is not None:
126 ui.note(_('making snapshot of %d files from rev %s\n') %
131 ui.note(_('making snapshot of %d files from rev %s\n') %
127 (len(files), short(node)))
132 (len(files), short(node)))
128 else:
133 else:
129 ui.note(_('making snapshot of %d files from working directory\n') %
134 ui.note(_('making snapshot of %d files from working directory\n') %
130 (len(files)))
135 (len(files)))
131
136
132 if files:
137 if files:
133 repo.ui.setconfig("ui", "archivemeta", False)
138 repo.ui.setconfig("ui", "archivemeta", False)
134
139
135 archival.archive(repo, base, node, 'files',
140 archival.archive(repo, base, node, 'files',
136 matchfn=scmutil.matchfiles(repo, files),
141 matchfn=scmutil.matchfiles(repo, files),
137 subrepos=listsubrepos)
142 subrepos=listsubrepos)
138
143
139 for fn in sorted(files):
144 for fn in sorted(files):
140 wfn = util.pconvert(fn)
145 wfn = util.pconvert(fn)
141 ui.note(' %s\n' % wfn)
146 ui.note(' %s\n' % wfn)
142
147
143 if node is None:
148 if node is None:
144 dest = os.path.join(base, wfn)
149 dest = os.path.join(base, wfn)
145
150
146 fnsandstat.append((dest, repo.wjoin(fn), os.lstat(dest)))
151 fnsandstat.append((dest, repo.wjoin(fn), os.lstat(dest)))
147 return dirname, fnsandstat
152 return dirname, fnsandstat
148
153
149 def dodiff(ui, repo, cmdline, pats, opts):
154 def dodiff(ui, repo, cmdline, pats, opts):
150 '''Do the actual diff:
155 '''Do the actual diff:
151
156
152 - copy to a temp structure if diffing 2 internal revisions
157 - copy to a temp structure if diffing 2 internal revisions
153 - copy to a temp structure if diffing working revision with
158 - copy to a temp structure if diffing working revision with
154 another one and more than 1 file is changed
159 another one and more than 1 file is changed
155 - just invoke the diff for a single file in the working dir
160 - just invoke the diff for a single file in the working dir
156 '''
161 '''
157
162
158 revs = opts.get('rev')
163 revs = opts.get('rev')
159 change = opts.get('change')
164 change = opts.get('change')
160 do3way = '$parent2' in cmdline
165 do3way = '$parent2' in cmdline
161
166
162 if revs and change:
167 if revs and change:
163 msg = _('cannot specify --rev and --change at the same time')
168 msg = _('cannot specify --rev and --change at the same time')
164 raise error.Abort(msg)
169 raise error.Abort(msg)
165 elif change:
170 elif change:
166 node2 = scmutil.revsingle(repo, change, None).node()
171 node2 = scmutil.revsingle(repo, change, None).node()
167 node1a, node1b = repo.changelog.parents(node2)
172 node1a, node1b = repo.changelog.parents(node2)
168 else:
173 else:
169 node1a, node2 = scmutil.revpair(repo, revs)
174 node1a, node2 = scmutil.revpair(repo, revs)
170 if not revs:
175 if not revs:
171 node1b = repo.dirstate.p2()
176 node1b = repo.dirstate.p2()
172 else:
177 else:
173 node1b = nullid
178 node1b = nullid
174
179
175 # Disable 3-way merge if there is only one parent
180 # Disable 3-way merge if there is only one parent
176 if do3way:
181 if do3way:
177 if node1b == nullid:
182 if node1b == nullid:
178 do3way = False
183 do3way = False
179
184
180 subrepos=opts.get('subrepos')
185 subrepos=opts.get('subrepos')
181
186
182 matcher = scmutil.match(repo[node2], pats, opts)
187 matcher = scmutil.match(repo[node2], pats, opts)
183
188
184 if opts.get('patch'):
189 if opts.get('patch'):
185 if subrepos:
190 if subrepos:
186 raise error.Abort(_('--patch cannot be used with --subrepos'))
191 raise error.Abort(_('--patch cannot be used with --subrepos'))
187 if node2 is None:
192 if node2 is None:
188 raise error.Abort(_('--patch requires two revisions'))
193 raise error.Abort(_('--patch requires two revisions'))
189 else:
194 else:
190 mod_a, add_a, rem_a = map(set, repo.status(node1a, node2, matcher,
195 mod_a, add_a, rem_a = map(set, repo.status(node1a, node2, matcher,
191 listsubrepos=subrepos)[:3])
196 listsubrepos=subrepos)[:3])
192 if do3way:
197 if do3way:
193 mod_b, add_b, rem_b = map(set,
198 mod_b, add_b, rem_b = map(set,
194 repo.status(node1b, node2, matcher,
199 repo.status(node1b, node2, matcher,
195 listsubrepos=subrepos)[:3])
200 listsubrepos=subrepos)[:3])
196 else:
201 else:
197 mod_b, add_b, rem_b = set(), set(), set()
202 mod_b, add_b, rem_b = set(), set(), set()
198 modadd = mod_a | add_a | mod_b | add_b
203 modadd = mod_a | add_a | mod_b | add_b
199 common = modadd | rem_a | rem_b
204 common = modadd | rem_a | rem_b
200 if not common:
205 if not common:
201 return 0
206 return 0
202
207
203 tmproot = tempfile.mkdtemp(prefix='extdiff.')
208 tmproot = tempfile.mkdtemp(prefix='extdiff.')
204 try:
209 try:
205 if not opts.get('patch'):
210 if not opts.get('patch'):
206 # Always make a copy of node1a (and node1b, if applicable)
211 # Always make a copy of node1a (and node1b, if applicable)
207 dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a)
212 dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a)
208 dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot,
213 dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot,
209 subrepos)[0]
214 subrepos)[0]
210 rev1a = '@%d' % repo[node1a].rev()
215 rev1a = '@%d' % repo[node1a].rev()
211 if do3way:
216 if do3way:
212 dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b)
217 dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b)
213 dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot,
218 dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot,
214 subrepos)[0]
219 subrepos)[0]
215 rev1b = '@%d' % repo[node1b].rev()
220 rev1b = '@%d' % repo[node1b].rev()
216 else:
221 else:
217 dir1b = None
222 dir1b = None
218 rev1b = ''
223 rev1b = ''
219
224
220 fnsandstat = []
225 fnsandstat = []
221
226
222 # If node2 in not the wc or there is >1 change, copy it
227 # If node2 in not the wc or there is >1 change, copy it
223 dir2root = ''
228 dir2root = ''
224 rev2 = ''
229 rev2 = ''
225 if node2:
230 if node2:
226 dir2 = snapshot(ui, repo, modadd, node2, tmproot, subrepos)[0]
231 dir2 = snapshot(ui, repo, modadd, node2, tmproot, subrepos)[0]
227 rev2 = '@%d' % repo[node2].rev()
232 rev2 = '@%d' % repo[node2].rev()
228 elif len(common) > 1:
233 elif len(common) > 1:
229 #we only actually need to get the files to copy back to
234 #we only actually need to get the files to copy back to
230 #the working dir in this case (because the other cases
235 #the working dir in this case (because the other cases
231 #are: diffing 2 revisions or single file -- in which case
236 #are: diffing 2 revisions or single file -- in which case
232 #the file is already directly passed to the diff tool).
237 #the file is already directly passed to the diff tool).
233 dir2, fnsandstat = snapshot(ui, repo, modadd, None, tmproot,
238 dir2, fnsandstat = snapshot(ui, repo, modadd, None, tmproot,
234 subrepos)
239 subrepos)
235 else:
240 else:
236 # This lets the diff tool open the changed file directly
241 # This lets the diff tool open the changed file directly
237 dir2 = ''
242 dir2 = ''
238 dir2root = repo.root
243 dir2root = repo.root
239
244
240 label1a = rev1a
245 label1a = rev1a
241 label1b = rev1b
246 label1b = rev1b
242 label2 = rev2
247 label2 = rev2
243
248
244 # If only one change, diff the files instead of the directories
249 # If only one change, diff the files instead of the directories
245 # Handle bogus modifies correctly by checking if the files exist
250 # Handle bogus modifies correctly by checking if the files exist
246 if len(common) == 1:
251 if len(common) == 1:
247 common_file = util.localpath(common.pop())
252 common_file = util.localpath(common.pop())
248 dir1a = os.path.join(tmproot, dir1a, common_file)
253 dir1a = os.path.join(tmproot, dir1a, common_file)
249 label1a = common_file + rev1a
254 label1a = common_file + rev1a
250 if not os.path.isfile(dir1a):
255 if not os.path.isfile(dir1a):
251 dir1a = os.devnull
256 dir1a = os.devnull
252 if do3way:
257 if do3way:
253 dir1b = os.path.join(tmproot, dir1b, common_file)
258 dir1b = os.path.join(tmproot, dir1b, common_file)
254 label1b = common_file + rev1b
259 label1b = common_file + rev1b
255 if not os.path.isfile(dir1b):
260 if not os.path.isfile(dir1b):
256 dir1b = os.devnull
261 dir1b = os.devnull
257 dir2 = os.path.join(dir2root, dir2, common_file)
262 dir2 = os.path.join(dir2root, dir2, common_file)
258 label2 = common_file + rev2
263 label2 = common_file + rev2
259 else:
264 else:
260 template = 'hg-%h.patch'
265 template = 'hg-%h.patch'
261 cmdutil.export(repo, [repo[node1a].rev(), repo[node2].rev()],
266 cmdutil.export(repo, [repo[node1a].rev(), repo[node2].rev()],
262 fntemplate=repo.vfs.reljoin(tmproot, template),
267 fntemplate=repo.vfs.reljoin(tmproot, template),
263 match=matcher)
268 match=matcher)
264 label1a = cmdutil.makefilename(repo[node1a], template)
269 label1a = cmdutil.makefilename(repo[node1a], template)
265 label2 = cmdutil.makefilename(repo[node2], template)
270 label2 = cmdutil.makefilename(repo[node2], template)
266 dir1a = repo.vfs.reljoin(tmproot, label1a)
271 dir1a = repo.vfs.reljoin(tmproot, label1a)
267 dir2 = repo.vfs.reljoin(tmproot, label2)
272 dir2 = repo.vfs.reljoin(tmproot, label2)
268 dir1b = None
273 dir1b = None
269 label1b = None
274 label1b = None
270 fnsandstat = []
275 fnsandstat = []
271
276
272 # Function to quote file/dir names in the argument string.
277 # Function to quote file/dir names in the argument string.
273 # When not operating in 3-way mode, an empty string is
278 # When not operating in 3-way mode, an empty string is
274 # returned for parent2
279 # returned for parent2
275 replace = {'parent': dir1a, 'parent1': dir1a, 'parent2': dir1b,
280 replace = {'parent': dir1a, 'parent1': dir1a, 'parent2': dir1b,
276 'plabel1': label1a, 'plabel2': label1b,
281 'plabel1': label1a, 'plabel2': label1b,
277 'clabel': label2, 'child': dir2,
282 'clabel': label2, 'child': dir2,
278 'root': repo.root}
283 'root': repo.root}
279 def quote(match):
284 def quote(match):
280 pre = match.group(2)
285 pre = match.group(2)
281 key = match.group(3)
286 key = match.group(3)
282 if not do3way and key == 'parent2':
287 if not do3way and key == 'parent2':
283 return pre
288 return pre
284 return pre + procutil.shellquote(replace[key])
289 return pre + procutil.shellquote(replace[key])
285
290
286 # Match parent2 first, so 'parent1?' will match both parent1 and parent
291 # Match parent2 first, so 'parent1?' will match both parent1 and parent
287 regex = (br'''(['"]?)([^\s'"$]*)'''
292 regex = (br'''(['"]?)([^\s'"$]*)'''
288 br'\$(parent2|parent1?|child|plabel1|plabel2|clabel|root)\1')
293 br'\$(parent2|parent1?|child|plabel1|plabel2|clabel|root)\1')
289 if not do3way and not re.search(regex, cmdline):
294 if not do3way and not re.search(regex, cmdline):
290 cmdline += ' $parent1 $child'
295 cmdline += ' $parent1 $child'
291 cmdline = re.sub(regex, quote, cmdline)
296 cmdline = re.sub(regex, quote, cmdline)
292
297
293 ui.debug('running %r in %s\n' % (pycompat.bytestr(cmdline), tmproot))
298 ui.debug('running %r in %s\n' % (pycompat.bytestr(cmdline), tmproot))
294 ui.system(cmdline, cwd=tmproot, blockedtag='extdiff')
299 ui.system(cmdline, cwd=tmproot, blockedtag='extdiff')
295
300
296 for copy_fn, working_fn, st in fnsandstat:
301 for copy_fn, working_fn, st in fnsandstat:
297 cpstat = os.lstat(copy_fn)
302 cpstat = os.lstat(copy_fn)
298 # Some tools copy the file and attributes, so mtime may not detect
303 # Some tools copy the file and attributes, so mtime may not detect
299 # all changes. A size check will detect more cases, but not all.
304 # all changes. A size check will detect more cases, but not all.
300 # The only certain way to detect every case is to diff all files,
305 # The only certain way to detect every case is to diff all files,
301 # which could be expensive.
306 # which could be expensive.
302 # copyfile() carries over the permission, so the mode check could
307 # copyfile() carries over the permission, so the mode check could
303 # be in an 'elif' branch, but for the case where the file has
308 # be in an 'elif' branch, but for the case where the file has
304 # changed without affecting mtime or size.
309 # changed without affecting mtime or size.
305 if (cpstat[stat.ST_MTIME] != st[stat.ST_MTIME]
310 if (cpstat[stat.ST_MTIME] != st[stat.ST_MTIME]
306 or cpstat.st_size != st.st_size
311 or cpstat.st_size != st.st_size
307 or (cpstat.st_mode & 0o100) != (st.st_mode & 0o100)):
312 or (cpstat.st_mode & 0o100) != (st.st_mode & 0o100)):
308 ui.debug('file changed while diffing. '
313 ui.debug('file changed while diffing. '
309 'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn))
314 'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn))
310 util.copyfile(copy_fn, working_fn)
315 util.copyfile(copy_fn, working_fn)
311
316
312 return 1
317 return 1
313 finally:
318 finally:
314 ui.note(_('cleaning up temp directory\n'))
319 ui.note(_('cleaning up temp directory\n'))
315 shutil.rmtree(tmproot)
320 shutil.rmtree(tmproot)
316
321
317 extdiffopts = [
322 extdiffopts = [
318 ('o', 'option', [],
323 ('o', 'option', [],
319 _('pass option to comparison program'), _('OPT')),
324 _('pass option to comparison program'), _('OPT')),
320 ('r', 'rev', [], _('revision'), _('REV')),
325 ('r', 'rev', [], _('revision'), _('REV')),
321 ('c', 'change', '', _('change made by revision'), _('REV')),
326 ('c', 'change', '', _('change made by revision'), _('REV')),
322 ('', 'patch', None, _('compare patches for two revisions'))
327 ('', 'patch', None, _('compare patches for two revisions'))
323 ] + cmdutil.walkopts + cmdutil.subrepoopts
328 ] + cmdutil.walkopts + cmdutil.subrepoopts
324
329
325 @command('extdiff',
330 @command('extdiff',
326 [('p', 'program', '', _('comparison program to run'), _('CMD')),
331 [('p', 'program', '', _('comparison program to run'), _('CMD')),
327 ] + extdiffopts,
332 ] + extdiffopts,
328 _('hg extdiff [OPT]... [FILE]...'),
333 _('hg extdiff [OPT]... [FILE]...'),
329 inferrepo=True)
334 inferrepo=True)
330 def extdiff(ui, repo, *pats, **opts):
335 def extdiff(ui, repo, *pats, **opts):
331 '''use external program to diff repository (or selected files)
336 '''use external program to diff repository (or selected files)
332
337
333 Show differences between revisions for the specified files, using
338 Show differences between revisions for the specified files, using
334 an external program. The default program used is diff, with
339 an external program. The default program used is diff, with
335 default options "-Npru".
340 default options "-Npru".
336
341
337 To select a different program, use the -p/--program option. The
342 To select a different program, use the -p/--program option. The
338 program will be passed the names of two directories to compare. To
343 program will be passed the names of two directories to compare. To
339 pass additional options to the program, use -o/--option. These
344 pass additional options to the program, use -o/--option. These
340 will be passed before the names of the directories to compare.
345 will be passed before the names of the directories to compare.
341
346
342 When two revision arguments are given, then changes are shown
347 When two revision arguments are given, then changes are shown
343 between those revisions. If only one revision is specified then
348 between those revisions. If only one revision is specified then
344 that revision is compared to the working directory, and, when no
349 that revision is compared to the working directory, and, when no
345 revisions are specified, the working directory files are compared
350 revisions are specified, the working directory files are compared
346 to its parent.'''
351 to its parent.'''
347 opts = pycompat.byteskwargs(opts)
352 opts = pycompat.byteskwargs(opts)
348 program = opts.get('program')
353 program = opts.get('program')
349 option = opts.get('option')
354 option = opts.get('option')
350 if not program:
355 if not program:
351 program = 'diff'
356 program = 'diff'
352 option = option or ['-Npru']
357 option = option or ['-Npru']
353 cmdline = ' '.join(map(procutil.shellquote, [program] + option))
358 cmdline = ' '.join(map(procutil.shellquote, [program] + option))
354 return dodiff(ui, repo, cmdline, pats, opts)
359 return dodiff(ui, repo, cmdline, pats, opts)
355
360
356 class savedcmd(object):
361 class savedcmd(object):
357 """use external program to diff repository (or selected files)
362 """use external program to diff repository (or selected files)
358
363
359 Show differences between revisions for the specified files, using
364 Show differences between revisions for the specified files, using
360 the following program::
365 the following program::
361
366
362 %(path)s
367 %(path)s
363
368
364 When two revision arguments are given, then changes are shown
369 When two revision arguments are given, then changes are shown
365 between those revisions. If only one revision is specified then
370 between those revisions. If only one revision is specified then
366 that revision is compared to the working directory, and, when no
371 that revision is compared to the working directory, and, when no
367 revisions are specified, the working directory files are compared
372 revisions are specified, the working directory files are compared
368 to its parent.
373 to its parent.
369 """
374 """
370
375
371 def __init__(self, path, cmdline):
376 def __init__(self, path, cmdline):
372 # We can't pass non-ASCII through docstrings (and path is
377 # We can't pass non-ASCII through docstrings (and path is
373 # in an unknown encoding anyway)
378 # in an unknown encoding anyway)
374 docpath = stringutil.escapestr(path)
379 docpath = stringutil.escapestr(path)
375 self.__doc__ %= {r'path': pycompat.sysstr(stringutil.uirepr(docpath))}
380 self.__doc__ %= {r'path': pycompat.sysstr(stringutil.uirepr(docpath))}
376 self._cmdline = cmdline
381 self._cmdline = cmdline
377
382
378 def __call__(self, ui, repo, *pats, **opts):
383 def __call__(self, ui, repo, *pats, **opts):
379 opts = pycompat.byteskwargs(opts)
384 opts = pycompat.byteskwargs(opts)
380 options = ' '.join(map(procutil.shellquote, opts['option']))
385 options = ' '.join(map(procutil.shellquote, opts['option']))
381 if options:
386 if options:
382 options = ' ' + options
387 options = ' ' + options
383 return dodiff(ui, repo, self._cmdline + options, pats, opts)
388 return dodiff(ui, repo, self._cmdline + options, pats, opts)
384
389
385 def uisetup(ui):
390 def uisetup(ui):
386 for cmd, path in ui.configitems('extdiff'):
391 for cmd, path in ui.configitems('extdiff'):
387 path = util.expandpath(path)
392 path = util.expandpath(path)
388 if cmd.startswith('cmd.'):
393 if cmd.startswith('cmd.'):
389 cmd = cmd[4:]
394 cmd = cmd[4:]
390 if not path:
395 if not path:
391 path = procutil.findexe(cmd)
396 path = procutil.findexe(cmd)
392 if path is None:
397 if path is None:
393 path = filemerge.findexternaltool(ui, cmd) or cmd
398 path = filemerge.findexternaltool(ui, cmd) or cmd
394 diffopts = ui.config('extdiff', 'opts.' + cmd)
399 diffopts = ui.config('extdiff', 'opts.' + cmd)
395 cmdline = procutil.shellquote(path)
400 cmdline = procutil.shellquote(path)
396 if diffopts:
401 if diffopts:
397 cmdline += ' ' + diffopts
402 cmdline += ' ' + diffopts
398 elif cmd.startswith('opts.'):
403 elif cmd.startswith('opts.'):
399 continue
404 continue
400 else:
405 else:
401 if path:
406 if path:
402 # case "cmd = path opts"
407 # case "cmd = path opts"
403 cmdline = path
408 cmdline = path
404 diffopts = len(pycompat.shlexsplit(cmdline)) > 1
409 diffopts = len(pycompat.shlexsplit(cmdline)) > 1
405 else:
410 else:
406 # case "cmd ="
411 # case "cmd ="
407 path = procutil.findexe(cmd)
412 path = procutil.findexe(cmd)
408 if path is None:
413 if path is None:
409 path = filemerge.findexternaltool(ui, cmd) or cmd
414 path = filemerge.findexternaltool(ui, cmd) or cmd
410 cmdline = procutil.shellquote(path)
415 cmdline = procutil.shellquote(path)
411 diffopts = False
416 diffopts = False
412 # look for diff arguments in [diff-tools] then [merge-tools]
417 # look for diff arguments in [diff-tools] then [merge-tools]
413 if not diffopts:
418 if not diffopts:
414 args = ui.config('diff-tools', cmd+'.diffargs') or \
419 args = ui.config('diff-tools', cmd+'.diffargs') or \
415 ui.config('merge-tools', cmd+'.diffargs')
420 ui.config('merge-tools', cmd+'.diffargs')
416 if args:
421 if args:
417 cmdline += ' ' + args
422 cmdline += ' ' + args
418 command(cmd, extdiffopts[:], _('hg %s [OPTION]... [FILE]...') % cmd,
423 command(cmd, extdiffopts[:], _('hg %s [OPTION]... [FILE]...') % cmd,
419 inferrepo=True)(savedcmd(path, cmdline))
424 inferrepo=True)(savedcmd(path, cmdline))
420
425
421 # tell hggettext to extract docstrings from these functions:
426 # tell hggettext to extract docstrings from these functions:
422 i18nfunctions = [savedcmd]
427 i18nfunctions = [savedcmd]
@@ -1,1714 +1,1718
1 Test basic extension support
1 Test basic extension support
2
2
3 $ cat > foobar.py <<EOF
3 $ cat > foobar.py <<EOF
4 > import os
4 > import os
5 > from mercurial import commands, registrar
5 > from mercurial import commands, registrar
6 > cmdtable = {}
6 > cmdtable = {}
7 > command = registrar.command(cmdtable)
7 > command = registrar.command(cmdtable)
8 > configtable = {}
8 > configtable = {}
9 > configitem = registrar.configitem(configtable)
9 > configitem = registrar.configitem(configtable)
10 > configitem(b'tests', b'foo', default=b"Foo")
10 > configitem(b'tests', b'foo', default=b"Foo")
11 > def uisetup(ui):
11 > def uisetup(ui):
12 > ui.write(b"uisetup called\\n")
12 > ui.write(b"uisetup called\\n")
13 > ui.flush()
13 > ui.flush()
14 > def reposetup(ui, repo):
14 > def reposetup(ui, repo):
15 > ui.write(b"reposetup called for %s\\n" % os.path.basename(repo.root))
15 > ui.write(b"reposetup called for %s\\n" % os.path.basename(repo.root))
16 > ui.write(b"ui %s= repo.ui\\n" % (ui == repo.ui and b"=" or b"!"))
16 > ui.write(b"ui %s= repo.ui\\n" % (ui == repo.ui and b"=" or b"!"))
17 > ui.flush()
17 > ui.flush()
18 > @command(b'foo', [], b'hg foo')
18 > @command(b'foo', [], b'hg foo')
19 > def foo(ui, *args, **kwargs):
19 > def foo(ui, *args, **kwargs):
20 > foo = ui.config(b'tests', b'foo')
20 > foo = ui.config(b'tests', b'foo')
21 > ui.write(foo)
21 > ui.write(foo)
22 > ui.write(b"\\n")
22 > ui.write(b"\\n")
23 > @command(b'bar', [], b'hg bar', norepo=True)
23 > @command(b'bar', [], b'hg bar', norepo=True)
24 > def bar(ui, *args, **kwargs):
24 > def bar(ui, *args, **kwargs):
25 > ui.write(b"Bar\\n")
25 > ui.write(b"Bar\\n")
26 > EOF
26 > EOF
27 $ abspath=`pwd`/foobar.py
27 $ abspath=`pwd`/foobar.py
28
28
29 $ mkdir barfoo
29 $ mkdir barfoo
30 $ cp foobar.py barfoo/__init__.py
30 $ cp foobar.py barfoo/__init__.py
31 $ barfoopath=`pwd`/barfoo
31 $ barfoopath=`pwd`/barfoo
32
32
33 $ hg init a
33 $ hg init a
34 $ cd a
34 $ cd a
35 $ echo foo > file
35 $ echo foo > file
36 $ hg add file
36 $ hg add file
37 $ hg commit -m 'add file'
37 $ hg commit -m 'add file'
38
38
39 $ echo '[extensions]' >> $HGRCPATH
39 $ echo '[extensions]' >> $HGRCPATH
40 $ echo "foobar = $abspath" >> $HGRCPATH
40 $ echo "foobar = $abspath" >> $HGRCPATH
41 $ hg foo
41 $ hg foo
42 uisetup called
42 uisetup called
43 reposetup called for a
43 reposetup called for a
44 ui == repo.ui
44 ui == repo.ui
45 reposetup called for a (chg !)
45 reposetup called for a (chg !)
46 ui == repo.ui (chg !)
46 ui == repo.ui (chg !)
47 Foo
47 Foo
48
48
49 $ cd ..
49 $ cd ..
50 $ hg clone a b
50 $ hg clone a b
51 uisetup called (no-chg !)
51 uisetup called (no-chg !)
52 reposetup called for a
52 reposetup called for a
53 ui == repo.ui
53 ui == repo.ui
54 reposetup called for b
54 reposetup called for b
55 ui == repo.ui
55 ui == repo.ui
56 updating to branch default
56 updating to branch default
57 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
57 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
58
58
59 $ hg bar
59 $ hg bar
60 uisetup called (no-chg !)
60 uisetup called (no-chg !)
61 Bar
61 Bar
62 $ echo 'foobar = !' >> $HGRCPATH
62 $ echo 'foobar = !' >> $HGRCPATH
63
63
64 module/__init__.py-style
64 module/__init__.py-style
65
65
66 $ echo "barfoo = $barfoopath" >> $HGRCPATH
66 $ echo "barfoo = $barfoopath" >> $HGRCPATH
67 $ cd a
67 $ cd a
68 $ hg foo
68 $ hg foo
69 uisetup called
69 uisetup called
70 reposetup called for a
70 reposetup called for a
71 ui == repo.ui
71 ui == repo.ui
72 reposetup called for a (chg !)
72 reposetup called for a (chg !)
73 ui == repo.ui (chg !)
73 ui == repo.ui (chg !)
74 Foo
74 Foo
75 $ echo 'barfoo = !' >> $HGRCPATH
75 $ echo 'barfoo = !' >> $HGRCPATH
76
76
77 Check that extensions are loaded in phases:
77 Check that extensions are loaded in phases:
78
78
79 $ cat > foo.py <<EOF
79 $ cat > foo.py <<EOF
80 > import os
80 > import os
81 > name = os.path.basename(__file__).rsplit('.', 1)[0]
81 > name = os.path.basename(__file__).rsplit('.', 1)[0]
82 > print("1) %s imported" % name)
82 > print("1) %s imported" % name)
83 > def uisetup(ui):
83 > def uisetup(ui):
84 > print("2) %s uisetup" % name)
84 > print("2) %s uisetup" % name)
85 > def extsetup():
85 > def extsetup():
86 > print("3) %s extsetup" % name)
86 > print("3) %s extsetup" % name)
87 > def reposetup(ui, repo):
87 > def reposetup(ui, repo):
88 > print("4) %s reposetup" % name)
88 > print("4) %s reposetup" % name)
89 >
89 >
90 > # custom predicate to check registration of functions at loading
90 > # custom predicate to check registration of functions at loading
91 > from mercurial import (
91 > from mercurial import (
92 > registrar,
92 > registrar,
93 > smartset,
93 > smartset,
94 > )
94 > )
95 > revsetpredicate = registrar.revsetpredicate()
95 > revsetpredicate = registrar.revsetpredicate()
96 > @revsetpredicate(name, safe=True) # safe=True for query via hgweb
96 > @revsetpredicate(name, safe=True) # safe=True for query via hgweb
97 > def custompredicate(repo, subset, x):
97 > def custompredicate(repo, subset, x):
98 > return smartset.baseset([r for r in subset if r in {0}])
98 > return smartset.baseset([r for r in subset if r in {0}])
99 > EOF
99 > EOF
100
100
101 $ cp foo.py bar.py
101 $ cp foo.py bar.py
102 $ echo 'foo = foo.py' >> $HGRCPATH
102 $ echo 'foo = foo.py' >> $HGRCPATH
103 $ echo 'bar = bar.py' >> $HGRCPATH
103 $ echo 'bar = bar.py' >> $HGRCPATH
104
104
105 Check normal command's load order of extensions and registration of functions
105 Check normal command's load order of extensions and registration of functions
106
106
107 $ hg log -r "foo() and bar()" -q
107 $ hg log -r "foo() and bar()" -q
108 1) foo imported
108 1) foo imported
109 1) bar imported
109 1) bar imported
110 2) foo uisetup
110 2) foo uisetup
111 2) bar uisetup
111 2) bar uisetup
112 3) foo extsetup
112 3) foo extsetup
113 3) bar extsetup
113 3) bar extsetup
114 4) foo reposetup
114 4) foo reposetup
115 4) bar reposetup
115 4) bar reposetup
116 0:c24b9ac61126
116 0:c24b9ac61126
117
117
118 Check hgweb's load order of extensions and registration of functions
118 Check hgweb's load order of extensions and registration of functions
119
119
120 $ cat > hgweb.cgi <<EOF
120 $ cat > hgweb.cgi <<EOF
121 > #!$PYTHON
121 > #!$PYTHON
122 > from mercurial import demandimport; demandimport.enable()
122 > from mercurial import demandimport; demandimport.enable()
123 > from mercurial.hgweb import hgweb
123 > from mercurial.hgweb import hgweb
124 > from mercurial.hgweb import wsgicgi
124 > from mercurial.hgweb import wsgicgi
125 > application = hgweb('.', 'test repo')
125 > application = hgweb('.', 'test repo')
126 > wsgicgi.launch(application)
126 > wsgicgi.launch(application)
127 > EOF
127 > EOF
128 $ . "$TESTDIR/cgienv"
128 $ . "$TESTDIR/cgienv"
129
129
130 $ PATH_INFO='/' SCRIPT_NAME='' $PYTHON hgweb.cgi \
130 $ PATH_INFO='/' SCRIPT_NAME='' $PYTHON hgweb.cgi \
131 > | grep '^[0-9]) ' # ignores HTML output
131 > | grep '^[0-9]) ' # ignores HTML output
132 1) foo imported
132 1) foo imported
133 1) bar imported
133 1) bar imported
134 2) foo uisetup
134 2) foo uisetup
135 2) bar uisetup
135 2) bar uisetup
136 3) foo extsetup
136 3) foo extsetup
137 3) bar extsetup
137 3) bar extsetup
138 4) foo reposetup
138 4) foo reposetup
139 4) bar reposetup
139 4) bar reposetup
140
140
141 (check that revset predicate foo() and bar() are available)
141 (check that revset predicate foo() and bar() are available)
142
142
143 #if msys
143 #if msys
144 $ PATH_INFO='//shortlog'
144 $ PATH_INFO='//shortlog'
145 #else
145 #else
146 $ PATH_INFO='/shortlog'
146 $ PATH_INFO='/shortlog'
147 #endif
147 #endif
148 $ export PATH_INFO
148 $ export PATH_INFO
149 $ SCRIPT_NAME='' QUERY_STRING='rev=foo() and bar()' $PYTHON hgweb.cgi \
149 $ SCRIPT_NAME='' QUERY_STRING='rev=foo() and bar()' $PYTHON hgweb.cgi \
150 > | grep '<a href="/rev/[0-9a-z]*">'
150 > | grep '<a href="/rev/[0-9a-z]*">'
151 <a href="/rev/c24b9ac61126">add file</a>
151 <a href="/rev/c24b9ac61126">add file</a>
152
152
153 $ echo 'foo = !' >> $HGRCPATH
153 $ echo 'foo = !' >> $HGRCPATH
154 $ echo 'bar = !' >> $HGRCPATH
154 $ echo 'bar = !' >> $HGRCPATH
155
155
156 Check "from __future__ import absolute_import" support for external libraries
156 Check "from __future__ import absolute_import" support for external libraries
157
157
158 #if windows
158 #if windows
159 $ PATHSEP=";"
159 $ PATHSEP=";"
160 #else
160 #else
161 $ PATHSEP=":"
161 $ PATHSEP=":"
162 #endif
162 #endif
163 $ export PATHSEP
163 $ export PATHSEP
164
164
165 $ mkdir $TESTTMP/libroot
165 $ mkdir $TESTTMP/libroot
166 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
166 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
167 $ mkdir $TESTTMP/libroot/mod
167 $ mkdir $TESTTMP/libroot/mod
168 $ touch $TESTTMP/libroot/mod/__init__.py
168 $ touch $TESTTMP/libroot/mod/__init__.py
169 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
169 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
170
170
171 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<EOF
171 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<EOF
172 > from __future__ import absolute_import
172 > from __future__ import absolute_import
173 > import ambig # should load "libroot/ambig.py"
173 > import ambig # should load "libroot/ambig.py"
174 > s = ambig.s
174 > s = ambig.s
175 > EOF
175 > EOF
176 $ cat > loadabs.py <<EOF
176 $ cat > loadabs.py <<EOF
177 > import mod.ambigabs as ambigabs
177 > import mod.ambigabs as ambigabs
178 > def extsetup():
178 > def extsetup():
179 > print('ambigabs.s=%s' % ambigabs.s)
179 > print('ambigabs.s=%s' % ambigabs.s)
180 > EOF
180 > EOF
181 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
181 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
182 ambigabs.s=libroot/ambig.py
182 ambigabs.s=libroot/ambig.py
183 $TESTTMP/a
183 $TESTTMP/a
184
184
185 #if no-py3k
185 #if no-py3k
186 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<EOF
186 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<EOF
187 > import ambig # should load "libroot/mod/ambig.py"
187 > import ambig # should load "libroot/mod/ambig.py"
188 > s = ambig.s
188 > s = ambig.s
189 > EOF
189 > EOF
190 $ cat > loadrel.py <<EOF
190 $ cat > loadrel.py <<EOF
191 > import mod.ambigrel as ambigrel
191 > import mod.ambigrel as ambigrel
192 > def extsetup():
192 > def extsetup():
193 > print('ambigrel.s=%s' % ambigrel.s)
193 > print('ambigrel.s=%s' % ambigrel.s)
194 > EOF
194 > EOF
195 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
195 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
196 ambigrel.s=libroot/mod/ambig.py
196 ambigrel.s=libroot/mod/ambig.py
197 $TESTTMP/a
197 $TESTTMP/a
198 #endif
198 #endif
199
199
200 Check absolute/relative import of extension specific modules
200 Check absolute/relative import of extension specific modules
201
201
202 $ mkdir $TESTTMP/extroot
202 $ mkdir $TESTTMP/extroot
203 $ cat > $TESTTMP/extroot/bar.py <<EOF
203 $ cat > $TESTTMP/extroot/bar.py <<EOF
204 > s = 'this is extroot.bar'
204 > s = 'this is extroot.bar'
205 > EOF
205 > EOF
206 $ mkdir $TESTTMP/extroot/sub1
206 $ mkdir $TESTTMP/extroot/sub1
207 $ cat > $TESTTMP/extroot/sub1/__init__.py <<EOF
207 $ cat > $TESTTMP/extroot/sub1/__init__.py <<EOF
208 > s = 'this is extroot.sub1.__init__'
208 > s = 'this is extroot.sub1.__init__'
209 > EOF
209 > EOF
210 $ cat > $TESTTMP/extroot/sub1/baz.py <<EOF
210 $ cat > $TESTTMP/extroot/sub1/baz.py <<EOF
211 > s = 'this is extroot.sub1.baz'
211 > s = 'this is extroot.sub1.baz'
212 > EOF
212 > EOF
213 $ cat > $TESTTMP/extroot/__init__.py <<EOF
213 $ cat > $TESTTMP/extroot/__init__.py <<EOF
214 > s = 'this is extroot.__init__'
214 > s = 'this is extroot.__init__'
215 > import foo
215 > import foo
216 > def extsetup(ui):
216 > def extsetup(ui):
217 > ui.write('(extroot) ', foo.func(), '\n')
217 > ui.write('(extroot) ', foo.func(), '\n')
218 > ui.flush()
218 > ui.flush()
219 > EOF
219 > EOF
220
220
221 $ cat > $TESTTMP/extroot/foo.py <<EOF
221 $ cat > $TESTTMP/extroot/foo.py <<EOF
222 > # test absolute import
222 > # test absolute import
223 > buf = []
223 > buf = []
224 > def func():
224 > def func():
225 > # "not locals" case
225 > # "not locals" case
226 > import extroot.bar
226 > import extroot.bar
227 > buf.append('import extroot.bar in func(): %s' % extroot.bar.s)
227 > buf.append('import extroot.bar in func(): %s' % extroot.bar.s)
228 > return '\n(extroot) '.join(buf)
228 > return '\n(extroot) '.join(buf)
229 > # "fromlist == ('*',)" case
229 > # "fromlist == ('*',)" case
230 > from extroot.bar import *
230 > from extroot.bar import *
231 > buf.append('from extroot.bar import *: %s' % s)
231 > buf.append('from extroot.bar import *: %s' % s)
232 > # "not fromlist" and "if '.' in name" case
232 > # "not fromlist" and "if '.' in name" case
233 > import extroot.sub1.baz
233 > import extroot.sub1.baz
234 > buf.append('import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
234 > buf.append('import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
235 > # "not fromlist" and NOT "if '.' in name" case
235 > # "not fromlist" and NOT "if '.' in name" case
236 > import extroot
236 > import extroot
237 > buf.append('import extroot: %s' % extroot.s)
237 > buf.append('import extroot: %s' % extroot.s)
238 > # NOT "not fromlist" and NOT "level != -1" case
238 > # NOT "not fromlist" and NOT "level != -1" case
239 > from extroot.bar import s
239 > from extroot.bar import s
240 > buf.append('from extroot.bar import s: %s' % s)
240 > buf.append('from extroot.bar import s: %s' % s)
241 > EOF
241 > EOF
242 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.extroot=$TESTTMP/extroot root)
242 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.extroot=$TESTTMP/extroot root)
243 (extroot) from extroot.bar import *: this is extroot.bar
243 (extroot) from extroot.bar import *: this is extroot.bar
244 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
244 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
245 (extroot) import extroot: this is extroot.__init__
245 (extroot) import extroot: this is extroot.__init__
246 (extroot) from extroot.bar import s: this is extroot.bar
246 (extroot) from extroot.bar import s: this is extroot.bar
247 (extroot) import extroot.bar in func(): this is extroot.bar
247 (extroot) import extroot.bar in func(): this is extroot.bar
248 $TESTTMP/a
248 $TESTTMP/a
249
249
250 #if no-py3k
250 #if no-py3k
251 $ rm "$TESTTMP"/extroot/foo.*
251 $ rm "$TESTTMP"/extroot/foo.*
252 $ rm -Rf "$TESTTMP/extroot/__pycache__"
252 $ rm -Rf "$TESTTMP/extroot/__pycache__"
253 $ cat > $TESTTMP/extroot/foo.py <<EOF
253 $ cat > $TESTTMP/extroot/foo.py <<EOF
254 > # test relative import
254 > # test relative import
255 > buf = []
255 > buf = []
256 > def func():
256 > def func():
257 > # "not locals" case
257 > # "not locals" case
258 > import bar
258 > import bar
259 > buf.append('import bar in func(): %s' % bar.s)
259 > buf.append('import bar in func(): %s' % bar.s)
260 > return '\n(extroot) '.join(buf)
260 > return '\n(extroot) '.join(buf)
261 > # "fromlist == ('*',)" case
261 > # "fromlist == ('*',)" case
262 > from bar import *
262 > from bar import *
263 > buf.append('from bar import *: %s' % s)
263 > buf.append('from bar import *: %s' % s)
264 > # "not fromlist" and "if '.' in name" case
264 > # "not fromlist" and "if '.' in name" case
265 > import sub1.baz
265 > import sub1.baz
266 > buf.append('import sub1.baz: %s' % sub1.baz.s)
266 > buf.append('import sub1.baz: %s' % sub1.baz.s)
267 > # "not fromlist" and NOT "if '.' in name" case
267 > # "not fromlist" and NOT "if '.' in name" case
268 > import sub1
268 > import sub1
269 > buf.append('import sub1: %s' % sub1.s)
269 > buf.append('import sub1: %s' % sub1.s)
270 > # NOT "not fromlist" and NOT "level != -1" case
270 > # NOT "not fromlist" and NOT "level != -1" case
271 > from bar import s
271 > from bar import s
272 > buf.append('from bar import s: %s' % s)
272 > buf.append('from bar import s: %s' % s)
273 > EOF
273 > EOF
274 $ hg --config extensions.extroot=$TESTTMP/extroot root
274 $ hg --config extensions.extroot=$TESTTMP/extroot root
275 (extroot) from bar import *: this is extroot.bar
275 (extroot) from bar import *: this is extroot.bar
276 (extroot) import sub1.baz: this is extroot.sub1.baz
276 (extroot) import sub1.baz: this is extroot.sub1.baz
277 (extroot) import sub1: this is extroot.sub1.__init__
277 (extroot) import sub1: this is extroot.sub1.__init__
278 (extroot) from bar import s: this is extroot.bar
278 (extroot) from bar import s: this is extroot.bar
279 (extroot) import bar in func(): this is extroot.bar
279 (extroot) import bar in func(): this is extroot.bar
280 $TESTTMP/a
280 $TESTTMP/a
281 #endif
281 #endif
282
282
283 #if demandimport
283 #if demandimport
284
284
285 Examine whether module loading is delayed until actual referring, even
285 Examine whether module loading is delayed until actual referring, even
286 though module is imported with "absolute_import" feature.
286 though module is imported with "absolute_import" feature.
287
287
288 Files below in each packages are used for described purpose:
288 Files below in each packages are used for described purpose:
289
289
290 - "called": examine whether "from MODULE import ATTR" works correctly
290 - "called": examine whether "from MODULE import ATTR" works correctly
291 - "unused": examine whether loading is delayed correctly
291 - "unused": examine whether loading is delayed correctly
292 - "used": examine whether "from PACKAGE import MODULE" works correctly
292 - "used": examine whether "from PACKAGE import MODULE" works correctly
293
293
294 Package hierarchy is needed to examine whether demand importing works
294 Package hierarchy is needed to examine whether demand importing works
295 as expected for "from SUB.PACK.AGE import MODULE".
295 as expected for "from SUB.PACK.AGE import MODULE".
296
296
297 Setup "external library" to be imported with "absolute_import"
297 Setup "external library" to be imported with "absolute_import"
298 feature.
298 feature.
299
299
300 $ mkdir -p $TESTTMP/extlibroot/lsub1/lsub2
300 $ mkdir -p $TESTTMP/extlibroot/lsub1/lsub2
301 $ touch $TESTTMP/extlibroot/__init__.py
301 $ touch $TESTTMP/extlibroot/__init__.py
302 $ touch $TESTTMP/extlibroot/lsub1/__init__.py
302 $ touch $TESTTMP/extlibroot/lsub1/__init__.py
303 $ touch $TESTTMP/extlibroot/lsub1/lsub2/__init__.py
303 $ touch $TESTTMP/extlibroot/lsub1/lsub2/__init__.py
304
304
305 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/called.py <<EOF
305 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/called.py <<EOF
306 > def func():
306 > def func():
307 > return "this is extlibroot.lsub1.lsub2.called.func()"
307 > return "this is extlibroot.lsub1.lsub2.called.func()"
308 > EOF
308 > EOF
309 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/unused.py <<EOF
309 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/unused.py <<EOF
310 > raise Exception("extlibroot.lsub1.lsub2.unused is loaded unintentionally")
310 > raise Exception("extlibroot.lsub1.lsub2.unused is loaded unintentionally")
311 > EOF
311 > EOF
312 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/used.py <<EOF
312 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/used.py <<EOF
313 > detail = "this is extlibroot.lsub1.lsub2.used"
313 > detail = "this is extlibroot.lsub1.lsub2.used"
314 > EOF
314 > EOF
315
315
316 Setup sub-package of "external library", which causes instantiation of
316 Setup sub-package of "external library", which causes instantiation of
317 demandmod in "recurse down the module chain" code path. Relative
317 demandmod in "recurse down the module chain" code path. Relative
318 importing with "absolute_import" feature isn't tested, because "level
318 importing with "absolute_import" feature isn't tested, because "level
319 >=1 " doesn't cause instantiation of demandmod.
319 >=1 " doesn't cause instantiation of demandmod.
320
320
321 $ mkdir -p $TESTTMP/extlibroot/recursedown/abs
321 $ mkdir -p $TESTTMP/extlibroot/recursedown/abs
322 $ cat > $TESTTMP/extlibroot/recursedown/abs/used.py <<EOF
322 $ cat > $TESTTMP/extlibroot/recursedown/abs/used.py <<EOF
323 > detail = "this is extlibroot.recursedown.abs.used"
323 > detail = "this is extlibroot.recursedown.abs.used"
324 > EOF
324 > EOF
325 $ cat > $TESTTMP/extlibroot/recursedown/abs/__init__.py <<EOF
325 $ cat > $TESTTMP/extlibroot/recursedown/abs/__init__.py <<EOF
326 > from __future__ import absolute_import
326 > from __future__ import absolute_import
327 > from extlibroot.recursedown.abs.used import detail
327 > from extlibroot.recursedown.abs.used import detail
328 > EOF
328 > EOF
329
329
330 $ mkdir -p $TESTTMP/extlibroot/recursedown/legacy
330 $ mkdir -p $TESTTMP/extlibroot/recursedown/legacy
331 $ cat > $TESTTMP/extlibroot/recursedown/legacy/used.py <<EOF
331 $ cat > $TESTTMP/extlibroot/recursedown/legacy/used.py <<EOF
332 > detail = "this is extlibroot.recursedown.legacy.used"
332 > detail = "this is extlibroot.recursedown.legacy.used"
333 > EOF
333 > EOF
334 $ cat > $TESTTMP/extlibroot/recursedown/legacy/__init__.py <<EOF
334 $ cat > $TESTTMP/extlibroot/recursedown/legacy/__init__.py <<EOF
335 > # legacy style (level == -1) import
335 > # legacy style (level == -1) import
336 > from extlibroot.recursedown.legacy.used import detail
336 > from extlibroot.recursedown.legacy.used import detail
337 > EOF
337 > EOF
338
338
339 $ cat > $TESTTMP/extlibroot/recursedown/__init__.py <<EOF
339 $ cat > $TESTTMP/extlibroot/recursedown/__init__.py <<EOF
340 > from __future__ import absolute_import
340 > from __future__ import absolute_import
341 > from extlibroot.recursedown.abs import detail as absdetail
341 > from extlibroot.recursedown.abs import detail as absdetail
342 > from .legacy import detail as legacydetail
342 > from .legacy import detail as legacydetail
343 > EOF
343 > EOF
344
344
345 Setup package that re-exports an attribute of its submodule as the same
345 Setup package that re-exports an attribute of its submodule as the same
346 name. This leaves 'shadowing.used' pointing to 'used.detail', but still
346 name. This leaves 'shadowing.used' pointing to 'used.detail', but still
347 the submodule 'used' should be somehow accessible. (issue5617)
347 the submodule 'used' should be somehow accessible. (issue5617)
348
348
349 $ mkdir -p $TESTTMP/extlibroot/shadowing
349 $ mkdir -p $TESTTMP/extlibroot/shadowing
350 $ cat > $TESTTMP/extlibroot/shadowing/used.py <<EOF
350 $ cat > $TESTTMP/extlibroot/shadowing/used.py <<EOF
351 > detail = "this is extlibroot.shadowing.used"
351 > detail = "this is extlibroot.shadowing.used"
352 > EOF
352 > EOF
353 $ cat > $TESTTMP/extlibroot/shadowing/proxied.py <<EOF
353 $ cat > $TESTTMP/extlibroot/shadowing/proxied.py <<EOF
354 > from __future__ import absolute_import
354 > from __future__ import absolute_import
355 > from extlibroot.shadowing.used import detail
355 > from extlibroot.shadowing.used import detail
356 > EOF
356 > EOF
357 $ cat > $TESTTMP/extlibroot/shadowing/__init__.py <<EOF
357 $ cat > $TESTTMP/extlibroot/shadowing/__init__.py <<EOF
358 > from __future__ import absolute_import
358 > from __future__ import absolute_import
359 > from .used import detail as used
359 > from .used import detail as used
360 > EOF
360 > EOF
361
361
362 Setup extension local modules to be imported with "absolute_import"
362 Setup extension local modules to be imported with "absolute_import"
363 feature.
363 feature.
364
364
365 $ mkdir -p $TESTTMP/absextroot/xsub1/xsub2
365 $ mkdir -p $TESTTMP/absextroot/xsub1/xsub2
366 $ touch $TESTTMP/absextroot/xsub1/__init__.py
366 $ touch $TESTTMP/absextroot/xsub1/__init__.py
367 $ touch $TESTTMP/absextroot/xsub1/xsub2/__init__.py
367 $ touch $TESTTMP/absextroot/xsub1/xsub2/__init__.py
368
368
369 $ cat > $TESTTMP/absextroot/xsub1/xsub2/called.py <<EOF
369 $ cat > $TESTTMP/absextroot/xsub1/xsub2/called.py <<EOF
370 > def func():
370 > def func():
371 > return "this is absextroot.xsub1.xsub2.called.func()"
371 > return "this is absextroot.xsub1.xsub2.called.func()"
372 > EOF
372 > EOF
373 $ cat > $TESTTMP/absextroot/xsub1/xsub2/unused.py <<EOF
373 $ cat > $TESTTMP/absextroot/xsub1/xsub2/unused.py <<EOF
374 > raise Exception("absextroot.xsub1.xsub2.unused is loaded unintentionally")
374 > raise Exception("absextroot.xsub1.xsub2.unused is loaded unintentionally")
375 > EOF
375 > EOF
376 $ cat > $TESTTMP/absextroot/xsub1/xsub2/used.py <<EOF
376 $ cat > $TESTTMP/absextroot/xsub1/xsub2/used.py <<EOF
377 > detail = "this is absextroot.xsub1.xsub2.used"
377 > detail = "this is absextroot.xsub1.xsub2.used"
378 > EOF
378 > EOF
379
379
380 Setup extension local modules to examine whether demand importing
380 Setup extension local modules to examine whether demand importing
381 works as expected in "level > 1" case.
381 works as expected in "level > 1" case.
382
382
383 $ cat > $TESTTMP/absextroot/relimportee.py <<EOF
383 $ cat > $TESTTMP/absextroot/relimportee.py <<EOF
384 > detail = "this is absextroot.relimportee"
384 > detail = "this is absextroot.relimportee"
385 > EOF
385 > EOF
386 $ cat > $TESTTMP/absextroot/xsub1/xsub2/relimporter.py <<EOF
386 $ cat > $TESTTMP/absextroot/xsub1/xsub2/relimporter.py <<EOF
387 > from __future__ import absolute_import
387 > from __future__ import absolute_import
388 > from ... import relimportee
388 > from ... import relimportee
389 > detail = "this relimporter imports %r" % (relimportee.detail)
389 > detail = "this relimporter imports %r" % (relimportee.detail)
390 > EOF
390 > EOF
391
391
392 Setup modules, which actually import extension local modules at
392 Setup modules, which actually import extension local modules at
393 runtime.
393 runtime.
394
394
395 $ cat > $TESTTMP/absextroot/absolute.py << EOF
395 $ cat > $TESTTMP/absextroot/absolute.py << EOF
396 > from __future__ import absolute_import
396 > from __future__ import absolute_import
397 >
397 >
398 > # import extension local modules absolutely (level = 0)
398 > # import extension local modules absolutely (level = 0)
399 > from absextroot.xsub1.xsub2 import used, unused
399 > from absextroot.xsub1.xsub2 import used, unused
400 > from absextroot.xsub1.xsub2.called import func
400 > from absextroot.xsub1.xsub2.called import func
401 >
401 >
402 > def getresult():
402 > def getresult():
403 > result = []
403 > result = []
404 > result.append(used.detail)
404 > result.append(used.detail)
405 > result.append(func())
405 > result.append(func())
406 > return result
406 > return result
407 > EOF
407 > EOF
408
408
409 $ cat > $TESTTMP/absextroot/relative.py << EOF
409 $ cat > $TESTTMP/absextroot/relative.py << EOF
410 > from __future__ import absolute_import
410 > from __future__ import absolute_import
411 >
411 >
412 > # import extension local modules relatively (level == 1)
412 > # import extension local modules relatively (level == 1)
413 > from .xsub1.xsub2 import used, unused
413 > from .xsub1.xsub2 import used, unused
414 > from .xsub1.xsub2.called import func
414 > from .xsub1.xsub2.called import func
415 >
415 >
416 > # import a module, which implies "importing with level > 1"
416 > # import a module, which implies "importing with level > 1"
417 > from .xsub1.xsub2 import relimporter
417 > from .xsub1.xsub2 import relimporter
418 >
418 >
419 > def getresult():
419 > def getresult():
420 > result = []
420 > result = []
421 > result.append(used.detail)
421 > result.append(used.detail)
422 > result.append(func())
422 > result.append(func())
423 > result.append(relimporter.detail)
423 > result.append(relimporter.detail)
424 > return result
424 > return result
425 > EOF
425 > EOF
426
426
427 Setup main procedure of extension.
427 Setup main procedure of extension.
428
428
429 $ cat > $TESTTMP/absextroot/__init__.py <<EOF
429 $ cat > $TESTTMP/absextroot/__init__.py <<EOF
430 > from __future__ import absolute_import
430 > from __future__ import absolute_import
431 > from mercurial import registrar
431 > from mercurial import registrar
432 > cmdtable = {}
432 > cmdtable = {}
433 > command = registrar.command(cmdtable)
433 > command = registrar.command(cmdtable)
434 >
434 >
435 > # "absolute" and "relative" shouldn't be imported before actual
435 > # "absolute" and "relative" shouldn't be imported before actual
436 > # command execution, because (1) they import same modules, and (2)
436 > # command execution, because (1) they import same modules, and (2)
437 > # preceding import (= instantiate "demandmod" object instead of
437 > # preceding import (= instantiate "demandmod" object instead of
438 > # real "module" object) might hide problem of succeeding import.
438 > # real "module" object) might hide problem of succeeding import.
439 >
439 >
440 > @command(b'showabsolute', [], norepo=True)
440 > @command(b'showabsolute', [], norepo=True)
441 > def showabsolute(ui, *args, **opts):
441 > def showabsolute(ui, *args, **opts):
442 > from absextroot import absolute
442 > from absextroot import absolute
443 > ui.write(b'ABS: %s\n' % '\nABS: '.join(absolute.getresult()))
443 > ui.write(b'ABS: %s\n' % '\nABS: '.join(absolute.getresult()))
444 >
444 >
445 > @command(b'showrelative', [], norepo=True)
445 > @command(b'showrelative', [], norepo=True)
446 > def showrelative(ui, *args, **opts):
446 > def showrelative(ui, *args, **opts):
447 > from . import relative
447 > from . import relative
448 > ui.write(b'REL: %s\n' % '\nREL: '.join(relative.getresult()))
448 > ui.write(b'REL: %s\n' % '\nREL: '.join(relative.getresult()))
449 >
449 >
450 > # import modules from external library
450 > # import modules from external library
451 > from extlibroot.lsub1.lsub2 import used as lused, unused as lunused
451 > from extlibroot.lsub1.lsub2 import used as lused, unused as lunused
452 > from extlibroot.lsub1.lsub2.called import func as lfunc
452 > from extlibroot.lsub1.lsub2.called import func as lfunc
453 > from extlibroot.recursedown import absdetail, legacydetail
453 > from extlibroot.recursedown import absdetail, legacydetail
454 > from extlibroot.shadowing import proxied
454 > from extlibroot.shadowing import proxied
455 >
455 >
456 > def uisetup(ui):
456 > def uisetup(ui):
457 > result = []
457 > result = []
458 > result.append(lused.detail)
458 > result.append(lused.detail)
459 > result.append(lfunc())
459 > result.append(lfunc())
460 > result.append(absdetail)
460 > result.append(absdetail)
461 > result.append(legacydetail)
461 > result.append(legacydetail)
462 > result.append(proxied.detail)
462 > result.append(proxied.detail)
463 > ui.write('LIB: %s\n' % '\nLIB: '.join(result))
463 > ui.write('LIB: %s\n' % '\nLIB: '.join(result))
464 > EOF
464 > EOF
465
465
466 Examine module importing.
466 Examine module importing.
467
467
468 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showabsolute)
468 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showabsolute)
469 LIB: this is extlibroot.lsub1.lsub2.used
469 LIB: this is extlibroot.lsub1.lsub2.used
470 LIB: this is extlibroot.lsub1.lsub2.called.func()
470 LIB: this is extlibroot.lsub1.lsub2.called.func()
471 LIB: this is extlibroot.recursedown.abs.used
471 LIB: this is extlibroot.recursedown.abs.used
472 LIB: this is extlibroot.recursedown.legacy.used
472 LIB: this is extlibroot.recursedown.legacy.used
473 LIB: this is extlibroot.shadowing.used
473 LIB: this is extlibroot.shadowing.used
474 ABS: this is absextroot.xsub1.xsub2.used
474 ABS: this is absextroot.xsub1.xsub2.used
475 ABS: this is absextroot.xsub1.xsub2.called.func()
475 ABS: this is absextroot.xsub1.xsub2.called.func()
476
476
477 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showrelative)
477 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showrelative)
478 LIB: this is extlibroot.lsub1.lsub2.used
478 LIB: this is extlibroot.lsub1.lsub2.used
479 LIB: this is extlibroot.lsub1.lsub2.called.func()
479 LIB: this is extlibroot.lsub1.lsub2.called.func()
480 LIB: this is extlibroot.recursedown.abs.used
480 LIB: this is extlibroot.recursedown.abs.used
481 LIB: this is extlibroot.recursedown.legacy.used
481 LIB: this is extlibroot.recursedown.legacy.used
482 LIB: this is extlibroot.shadowing.used
482 LIB: this is extlibroot.shadowing.used
483 REL: this is absextroot.xsub1.xsub2.used
483 REL: this is absextroot.xsub1.xsub2.used
484 REL: this is absextroot.xsub1.xsub2.called.func()
484 REL: this is absextroot.xsub1.xsub2.called.func()
485 REL: this relimporter imports 'this is absextroot.relimportee'
485 REL: this relimporter imports 'this is absextroot.relimportee'
486
486
487 Examine whether sub-module is imported relatively as expected.
487 Examine whether sub-module is imported relatively as expected.
488
488
489 See also issue5208 for detail about example case on Python 3.x.
489 See also issue5208 for detail about example case on Python 3.x.
490
490
491 $ f -q $TESTTMP/extlibroot/lsub1/lsub2/notexist.py
491 $ f -q $TESTTMP/extlibroot/lsub1/lsub2/notexist.py
492 $TESTTMP/extlibroot/lsub1/lsub2/notexist.py: file not found
492 $TESTTMP/extlibroot/lsub1/lsub2/notexist.py: file not found
493
493
494 $ cat > $TESTTMP/notexist.py <<EOF
494 $ cat > $TESTTMP/notexist.py <<EOF
495 > text = 'notexist.py at root is loaded unintentionally\n'
495 > text = 'notexist.py at root is loaded unintentionally\n'
496 > EOF
496 > EOF
497
497
498 $ cat > $TESTTMP/checkrelativity.py <<EOF
498 $ cat > $TESTTMP/checkrelativity.py <<EOF
499 > from mercurial import registrar
499 > from mercurial import registrar
500 > cmdtable = {}
500 > cmdtable = {}
501 > command = registrar.command(cmdtable)
501 > command = registrar.command(cmdtable)
502 >
502 >
503 > # demand import avoids failure of importing notexist here
503 > # demand import avoids failure of importing notexist here
504 > import extlibroot.lsub1.lsub2.notexist
504 > import extlibroot.lsub1.lsub2.notexist
505 >
505 >
506 > @command(b'checkrelativity', [], norepo=True)
506 > @command(b'checkrelativity', [], norepo=True)
507 > def checkrelativity(ui, *args, **opts):
507 > def checkrelativity(ui, *args, **opts):
508 > try:
508 > try:
509 > ui.write(extlibroot.lsub1.lsub2.notexist.text)
509 > ui.write(extlibroot.lsub1.lsub2.notexist.text)
510 > return 1 # unintentional success
510 > return 1 # unintentional success
511 > except ImportError:
511 > except ImportError:
512 > pass # intentional failure
512 > pass # intentional failure
513 > EOF
513 > EOF
514
514
515 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.checkrelativity=$TESTTMP/checkrelativity.py checkrelativity)
515 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.checkrelativity=$TESTTMP/checkrelativity.py checkrelativity)
516
516
517 #endif
517 #endif
518
518
519 Make sure a broken uisetup doesn't globally break hg:
519 Make sure a broken uisetup doesn't globally break hg:
520 $ cat > $TESTTMP/baduisetup.py <<EOF
520 $ cat > $TESTTMP/baduisetup.py <<EOF
521 > def uisetup(ui):
521 > def uisetup(ui):
522 > 1/0
522 > 1/0
523 > EOF
523 > EOF
524
524
525 Even though the extension fails during uisetup, hg is still basically usable:
525 Even though the extension fails during uisetup, hg is still basically usable:
526 $ hg --config extensions.baduisetup=$TESTTMP/baduisetup.py version
526 $ hg --config extensions.baduisetup=$TESTTMP/baduisetup.py version
527 Traceback (most recent call last):
527 Traceback (most recent call last):
528 File "*/mercurial/extensions.py", line *, in _runuisetup (glob)
528 File "*/mercurial/extensions.py", line *, in _runuisetup (glob)
529 uisetup(ui)
529 uisetup(ui)
530 File "$TESTTMP/baduisetup.py", line 2, in uisetup
530 File "$TESTTMP/baduisetup.py", line 2, in uisetup
531 1/0
531 1/0
532 ZeroDivisionError: integer division or modulo by zero
532 ZeroDivisionError: integer division or modulo by zero
533 *** failed to set up extension baduisetup: integer division or modulo by zero
533 *** failed to set up extension baduisetup: integer division or modulo by zero
534 Mercurial Distributed SCM (version *) (glob)
534 Mercurial Distributed SCM (version *) (glob)
535 (see https://mercurial-scm.org for more information)
535 (see https://mercurial-scm.org for more information)
536
536
537 Copyright (C) 2005-* Matt Mackall and others (glob)
537 Copyright (C) 2005-* Matt Mackall and others (glob)
538 This is free software; see the source for copying conditions. There is NO
538 This is free software; see the source for copying conditions. There is NO
539 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
539 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
540
540
541 $ cd ..
541 $ cd ..
542
542
543 hide outer repo
543 hide outer repo
544 $ hg init
544 $ hg init
545
545
546 $ cat > empty.py <<EOF
546 $ cat > empty.py <<EOF
547 > '''empty cmdtable
547 > '''empty cmdtable
548 > '''
548 > '''
549 > cmdtable = {}
549 > cmdtable = {}
550 > EOF
550 > EOF
551 $ emptypath=`pwd`/empty.py
551 $ emptypath=`pwd`/empty.py
552 $ echo "empty = $emptypath" >> $HGRCPATH
552 $ echo "empty = $emptypath" >> $HGRCPATH
553 $ hg help empty
553 $ hg help empty
554 empty extension - empty cmdtable
554 empty extension - empty cmdtable
555
555
556 no commands defined
556 no commands defined
557
557
558
558
559 $ echo 'empty = !' >> $HGRCPATH
559 $ echo 'empty = !' >> $HGRCPATH
560
560
561 $ cat > debugextension.py <<EOF
561 $ cat > debugextension.py <<EOF
562 > '''only debugcommands
562 > '''only debugcommands
563 > '''
563 > '''
564 > from mercurial import registrar
564 > from mercurial import registrar
565 > cmdtable = {}
565 > cmdtable = {}
566 > command = registrar.command(cmdtable)
566 > command = registrar.command(cmdtable)
567 > @command(b'debugfoobar', [], b'hg debugfoobar')
567 > @command(b'debugfoobar', [], b'hg debugfoobar')
568 > def debugfoobar(ui, repo, *args, **opts):
568 > def debugfoobar(ui, repo, *args, **opts):
569 > "yet another debug command"
569 > "yet another debug command"
570 > pass
570 > pass
571 > @command(b'foo', [], b'hg foo')
571 > @command(b'foo', [], b'hg foo')
572 > def foo(ui, repo, *args, **opts):
572 > def foo(ui, repo, *args, **opts):
573 > """yet another foo command
573 > """yet another foo command
574 > This command has been DEPRECATED since forever.
574 > This command has been DEPRECATED since forever.
575 > """
575 > """
576 > pass
576 > pass
577 > EOF
577 > EOF
578 $ debugpath=`pwd`/debugextension.py
578 $ debugpath=`pwd`/debugextension.py
579 $ echo "debugextension = $debugpath" >> $HGRCPATH
579 $ echo "debugextension = $debugpath" >> $HGRCPATH
580
580
581 $ hg help debugextension
581 $ hg help debugextension
582 hg debugextensions
582 hg debugextensions
583
583
584 show information about active extensions
584 show information about active extensions
585
585
586 options:
586 options:
587
587
588 (some details hidden, use --verbose to show complete help)
588 (some details hidden, use --verbose to show complete help)
589
589
590
590
591 $ hg --verbose help debugextension
591 $ hg --verbose help debugextension
592 hg debugextensions
592 hg debugextensions
593
593
594 show information about active extensions
594 show information about active extensions
595
595
596 options:
596 options:
597
597
598 -T --template TEMPLATE display with template (EXPERIMENTAL)
598 -T --template TEMPLATE display with template (EXPERIMENTAL)
599
599
600 global options ([+] can be repeated):
600 global options ([+] can be repeated):
601
601
602 -R --repository REPO repository root directory or name of overlay bundle
602 -R --repository REPO repository root directory or name of overlay bundle
603 file
603 file
604 --cwd DIR change working directory
604 --cwd DIR change working directory
605 -y --noninteractive do not prompt, automatically pick the first choice for
605 -y --noninteractive do not prompt, automatically pick the first choice for
606 all prompts
606 all prompts
607 -q --quiet suppress output
607 -q --quiet suppress output
608 -v --verbose enable additional output
608 -v --verbose enable additional output
609 --color TYPE when to colorize (boolean, always, auto, never, or
609 --color TYPE when to colorize (boolean, always, auto, never, or
610 debug)
610 debug)
611 --config CONFIG [+] set/override config option (use 'section.name=value')
611 --config CONFIG [+] set/override config option (use 'section.name=value')
612 --debug enable debugging output
612 --debug enable debugging output
613 --debugger start debugger
613 --debugger start debugger
614 --encoding ENCODE set the charset encoding (default: ascii)
614 --encoding ENCODE set the charset encoding (default: ascii)
615 --encodingmode MODE set the charset encoding mode (default: strict)
615 --encodingmode MODE set the charset encoding mode (default: strict)
616 --traceback always print a traceback on exception
616 --traceback always print a traceback on exception
617 --time time how long the command takes
617 --time time how long the command takes
618 --profile print command execution profile
618 --profile print command execution profile
619 --version output version information and exit
619 --version output version information and exit
620 -h --help display help and exit
620 -h --help display help and exit
621 --hidden consider hidden changesets
621 --hidden consider hidden changesets
622 --pager TYPE when to paginate (boolean, always, auto, or never)
622 --pager TYPE when to paginate (boolean, always, auto, or never)
623 (default: auto)
623 (default: auto)
624
624
625
625
626
626
627
627
628
628
629
629
630 $ hg --debug help debugextension
630 $ hg --debug help debugextension
631 hg debugextensions
631 hg debugextensions
632
632
633 show information about active extensions
633 show information about active extensions
634
634
635 options:
635 options:
636
636
637 -T --template TEMPLATE display with template (EXPERIMENTAL)
637 -T --template TEMPLATE display with template (EXPERIMENTAL)
638
638
639 global options ([+] can be repeated):
639 global options ([+] can be repeated):
640
640
641 -R --repository REPO repository root directory or name of overlay bundle
641 -R --repository REPO repository root directory or name of overlay bundle
642 file
642 file
643 --cwd DIR change working directory
643 --cwd DIR change working directory
644 -y --noninteractive do not prompt, automatically pick the first choice for
644 -y --noninteractive do not prompt, automatically pick the first choice for
645 all prompts
645 all prompts
646 -q --quiet suppress output
646 -q --quiet suppress output
647 -v --verbose enable additional output
647 -v --verbose enable additional output
648 --color TYPE when to colorize (boolean, always, auto, never, or
648 --color TYPE when to colorize (boolean, always, auto, never, or
649 debug)
649 debug)
650 --config CONFIG [+] set/override config option (use 'section.name=value')
650 --config CONFIG [+] set/override config option (use 'section.name=value')
651 --debug enable debugging output
651 --debug enable debugging output
652 --debugger start debugger
652 --debugger start debugger
653 --encoding ENCODE set the charset encoding (default: ascii)
653 --encoding ENCODE set the charset encoding (default: ascii)
654 --encodingmode MODE set the charset encoding mode (default: strict)
654 --encodingmode MODE set the charset encoding mode (default: strict)
655 --traceback always print a traceback on exception
655 --traceback always print a traceback on exception
656 --time time how long the command takes
656 --time time how long the command takes
657 --profile print command execution profile
657 --profile print command execution profile
658 --version output version information and exit
658 --version output version information and exit
659 -h --help display help and exit
659 -h --help display help and exit
660 --hidden consider hidden changesets
660 --hidden consider hidden changesets
661 --pager TYPE when to paginate (boolean, always, auto, or never)
661 --pager TYPE when to paginate (boolean, always, auto, or never)
662 (default: auto)
662 (default: auto)
663
663
664
664
665
665
666
666
667
667
668 $ echo 'debugextension = !' >> $HGRCPATH
668 $ echo 'debugextension = !' >> $HGRCPATH
669
669
670 Asking for help about a deprecated extension should do something useful:
670 Asking for help about a deprecated extension should do something useful:
671
671
672 $ hg help glog
672 $ hg help glog
673 'glog' is provided by the following extension:
673 'glog' is provided by the following extension:
674
674
675 graphlog command to view revision graphs from a shell (DEPRECATED)
675 graphlog command to view revision graphs from a shell (DEPRECATED)
676
676
677 (use 'hg help extensions' for information on enabling extensions)
677 (use 'hg help extensions' for information on enabling extensions)
678
678
679 Extension module help vs command help:
679 Extension module help vs command help:
680
680
681 $ echo 'extdiff =' >> $HGRCPATH
681 $ echo 'extdiff =' >> $HGRCPATH
682 $ hg help extdiff
682 $ hg help extdiff
683 hg extdiff [OPT]... [FILE]...
683 hg extdiff [OPT]... [FILE]...
684
684
685 use external program to diff repository (or selected files)
685 use external program to diff repository (or selected files)
686
686
687 Show differences between revisions for the specified files, using an
687 Show differences between revisions for the specified files, using an
688 external program. The default program used is diff, with default options
688 external program. The default program used is diff, with default options
689 "-Npru".
689 "-Npru".
690
690
691 To select a different program, use the -p/--program option. The program
691 To select a different program, use the -p/--program option. The program
692 will be passed the names of two directories to compare. To pass additional
692 will be passed the names of two directories to compare. To pass additional
693 options to the program, use -o/--option. These will be passed before the
693 options to the program, use -o/--option. These will be passed before the
694 names of the directories to compare.
694 names of the directories to compare.
695
695
696 When two revision arguments are given, then changes are shown between
696 When two revision arguments are given, then changes are shown between
697 those revisions. If only one revision is specified then that revision is
697 those revisions. If only one revision is specified then that revision is
698 compared to the working directory, and, when no revisions are specified,
698 compared to the working directory, and, when no revisions are specified,
699 the working directory files are compared to its parent.
699 the working directory files are compared to its parent.
700
700
701 (use 'hg help -e extdiff' to show help for the extdiff extension)
701 (use 'hg help -e extdiff' to show help for the extdiff extension)
702
702
703 options ([+] can be repeated):
703 options ([+] can be repeated):
704
704
705 -p --program CMD comparison program to run
705 -p --program CMD comparison program to run
706 -o --option OPT [+] pass option to comparison program
706 -o --option OPT [+] pass option to comparison program
707 -r --rev REV [+] revision
707 -r --rev REV [+] revision
708 -c --change REV change made by revision
708 -c --change REV change made by revision
709 --patch compare patches for two revisions
709 --patch compare patches for two revisions
710 -I --include PATTERN [+] include names matching the given patterns
710 -I --include PATTERN [+] include names matching the given patterns
711 -X --exclude PATTERN [+] exclude names matching the given patterns
711 -X --exclude PATTERN [+] exclude names matching the given patterns
712 -S --subrepos recurse into subrepositories
712 -S --subrepos recurse into subrepositories
713
713
714 (some details hidden, use --verbose to show complete help)
714 (some details hidden, use --verbose to show complete help)
715
715
716
716
717
717
718
718
719
719
720
720
721
721
722
722
723
723
724
724
725 $ hg help --extension extdiff
725 $ hg help --extension extdiff
726 extdiff extension - command to allow external programs to compare revisions
726 extdiff extension - command to allow external programs to compare revisions
727
727
728 The extdiff Mercurial extension allows you to use external programs to compare
728 The extdiff Mercurial extension allows you to use external programs to compare
729 revisions, or revision with working directory. The external diff programs are
729 revisions, or revision with working directory. The external diff programs are
730 called with a configurable set of options and two non-option arguments: paths
730 called with a configurable set of options and two non-option arguments: paths
731 to directories containing snapshots of files to compare.
731 to directories containing snapshots of files to compare.
732
732
733 If there is more than one file being compared and the "child" revision is the
734 working directory, any modifications made in the external diff program will be
735 copied back to the working directory from the temporary directory.
736
733 The extdiff extension also allows you to configure new diff commands, so you
737 The extdiff extension also allows you to configure new diff commands, so you
734 do not need to type 'hg extdiff -p kdiff3' always.
738 do not need to type 'hg extdiff -p kdiff3' always.
735
739
736 [extdiff]
740 [extdiff]
737 # add new command that runs GNU diff(1) in 'context diff' mode
741 # add new command that runs GNU diff(1) in 'context diff' mode
738 cdiff = gdiff -Nprc5
742 cdiff = gdiff -Nprc5
739 ## or the old way:
743 ## or the old way:
740 #cmd.cdiff = gdiff
744 #cmd.cdiff = gdiff
741 #opts.cdiff = -Nprc5
745 #opts.cdiff = -Nprc5
742
746
743 # add new command called meld, runs meld (no need to name twice). If
747 # add new command called meld, runs meld (no need to name twice). If
744 # the meld executable is not available, the meld tool in [merge-tools]
748 # the meld executable is not available, the meld tool in [merge-tools]
745 # will be used, if available
749 # will be used, if available
746 meld =
750 meld =
747
751
748 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
752 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
749 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
753 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
750 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
754 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
751 # your .vimrc
755 # your .vimrc
752 vimdiff = gvim -f "+next" \
756 vimdiff = gvim -f "+next" \
753 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
757 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
754
758
755 Tool arguments can include variables that are expanded at runtime:
759 Tool arguments can include variables that are expanded at runtime:
756
760
757 $parent1, $plabel1 - filename, descriptive label of first parent
761 $parent1, $plabel1 - filename, descriptive label of first parent
758 $child, $clabel - filename, descriptive label of child revision
762 $child, $clabel - filename, descriptive label of child revision
759 $parent2, $plabel2 - filename, descriptive label of second parent
763 $parent2, $plabel2 - filename, descriptive label of second parent
760 $root - repository root
764 $root - repository root
761 $parent is an alias for $parent1.
765 $parent is an alias for $parent1.
762
766
763 The extdiff extension will look in your [diff-tools] and [merge-tools]
767 The extdiff extension will look in your [diff-tools] and [merge-tools]
764 sections for diff tool arguments, when none are specified in [extdiff].
768 sections for diff tool arguments, when none are specified in [extdiff].
765
769
766 [extdiff]
770 [extdiff]
767 kdiff3 =
771 kdiff3 =
768
772
769 [diff-tools]
773 [diff-tools]
770 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
774 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
771
775
772 You can use -I/-X and list of file or directory names like normal 'hg diff'
776 You can use -I/-X and list of file or directory names like normal 'hg diff'
773 command. The extdiff extension makes snapshots of only needed files, so
777 command. The extdiff extension makes snapshots of only needed files, so
774 running the external diff program will actually be pretty fast (at least
778 running the external diff program will actually be pretty fast (at least
775 faster than having to compare the entire tree).
779 faster than having to compare the entire tree).
776
780
777 list of commands:
781 list of commands:
778
782
779 extdiff use external program to diff repository (or selected files)
783 extdiff use external program to diff repository (or selected files)
780
784
781 (use 'hg help -v -e extdiff' to show built-in aliases and global options)
785 (use 'hg help -v -e extdiff' to show built-in aliases and global options)
782
786
783
787
784
788
785
789
786
790
787
791
788
792
789
793
790
794
791
795
792
796
793
797
794
798
795
799
796
800
797
801
798 $ echo 'extdiff = !' >> $HGRCPATH
802 $ echo 'extdiff = !' >> $HGRCPATH
799
803
800 Test help topic with same name as extension
804 Test help topic with same name as extension
801
805
802 $ cat > multirevs.py <<EOF
806 $ cat > multirevs.py <<EOF
803 > from mercurial import commands, registrar
807 > from mercurial import commands, registrar
804 > cmdtable = {}
808 > cmdtable = {}
805 > command = registrar.command(cmdtable)
809 > command = registrar.command(cmdtable)
806 > """multirevs extension
810 > """multirevs extension
807 > Big multi-line module docstring."""
811 > Big multi-line module docstring."""
808 > @command(b'multirevs', [], b'ARG', norepo=True)
812 > @command(b'multirevs', [], b'ARG', norepo=True)
809 > def multirevs(ui, repo, arg, *args, **opts):
813 > def multirevs(ui, repo, arg, *args, **opts):
810 > """multirevs command"""
814 > """multirevs command"""
811 > pass
815 > pass
812 > EOF
816 > EOF
813 $ echo "multirevs = multirevs.py" >> $HGRCPATH
817 $ echo "multirevs = multirevs.py" >> $HGRCPATH
814
818
815 $ hg help multirevs | tail
819 $ hg help multirevs | tail
816 used):
820 used):
817
821
818 hg update :@
822 hg update :@
819
823
820 - Show diff between tags 1.3 and 1.5 (this works because the first and the
824 - Show diff between tags 1.3 and 1.5 (this works because the first and the
821 last revisions of the revset are used):
825 last revisions of the revset are used):
822
826
823 hg diff -r 1.3::1.5
827 hg diff -r 1.3::1.5
824
828
825 use 'hg help -c multirevs' to see help for the multirevs command
829 use 'hg help -c multirevs' to see help for the multirevs command
826
830
827
831
828
832
829
833
830
834
831
835
832 $ hg help -c multirevs
836 $ hg help -c multirevs
833 hg multirevs ARG
837 hg multirevs ARG
834
838
835 multirevs command
839 multirevs command
836
840
837 (some details hidden, use --verbose to show complete help)
841 (some details hidden, use --verbose to show complete help)
838
842
839
843
840
844
841 $ hg multirevs
845 $ hg multirevs
842 hg multirevs: invalid arguments
846 hg multirevs: invalid arguments
843 hg multirevs ARG
847 hg multirevs ARG
844
848
845 multirevs command
849 multirevs command
846
850
847 (use 'hg multirevs -h' to show more help)
851 (use 'hg multirevs -h' to show more help)
848 [255]
852 [255]
849
853
850
854
851
855
852 $ echo "multirevs = !" >> $HGRCPATH
856 $ echo "multirevs = !" >> $HGRCPATH
853
857
854 Issue811: Problem loading extensions twice (by site and by user)
858 Issue811: Problem loading extensions twice (by site and by user)
855
859
856 $ cat <<EOF >> $HGRCPATH
860 $ cat <<EOF >> $HGRCPATH
857 > mq =
861 > mq =
858 > strip =
862 > strip =
859 > hgext.mq =
863 > hgext.mq =
860 > hgext/mq =
864 > hgext/mq =
861 > EOF
865 > EOF
862
866
863 Show extensions:
867 Show extensions:
864 (note that mq force load strip, also checking it's not loaded twice)
868 (note that mq force load strip, also checking it's not loaded twice)
865
869
866 $ hg debugextensions
870 $ hg debugextensions
867 mq
871 mq
868 strip
872 strip
869
873
870 For extensions, which name matches one of its commands, help
874 For extensions, which name matches one of its commands, help
871 message should ask '-v -e' to get list of built-in aliases
875 message should ask '-v -e' to get list of built-in aliases
872 along with extension help itself
876 along with extension help itself
873
877
874 $ mkdir $TESTTMP/d
878 $ mkdir $TESTTMP/d
875 $ cat > $TESTTMP/d/dodo.py <<EOF
879 $ cat > $TESTTMP/d/dodo.py <<EOF
876 > """
880 > """
877 > This is an awesome 'dodo' extension. It does nothing and
881 > This is an awesome 'dodo' extension. It does nothing and
878 > writes 'Foo foo'
882 > writes 'Foo foo'
879 > """
883 > """
880 > from mercurial import commands, registrar
884 > from mercurial import commands, registrar
881 > cmdtable = {}
885 > cmdtable = {}
882 > command = registrar.command(cmdtable)
886 > command = registrar.command(cmdtable)
883 > @command(b'dodo', [], b'hg dodo')
887 > @command(b'dodo', [], b'hg dodo')
884 > def dodo(ui, *args, **kwargs):
888 > def dodo(ui, *args, **kwargs):
885 > """Does nothing"""
889 > """Does nothing"""
886 > ui.write(b"I do nothing. Yay\\n")
890 > ui.write(b"I do nothing. Yay\\n")
887 > @command(b'foofoo', [], b'hg foofoo')
891 > @command(b'foofoo', [], b'hg foofoo')
888 > def foofoo(ui, *args, **kwargs):
892 > def foofoo(ui, *args, **kwargs):
889 > """Writes 'Foo foo'"""
893 > """Writes 'Foo foo'"""
890 > ui.write(b"Foo foo\\n")
894 > ui.write(b"Foo foo\\n")
891 > EOF
895 > EOF
892 $ dodopath=$TESTTMP/d/dodo.py
896 $ dodopath=$TESTTMP/d/dodo.py
893
897
894 $ echo "dodo = $dodopath" >> $HGRCPATH
898 $ echo "dodo = $dodopath" >> $HGRCPATH
895
899
896 Make sure that user is asked to enter '-v -e' to get list of built-in aliases
900 Make sure that user is asked to enter '-v -e' to get list of built-in aliases
897 $ hg help -e dodo
901 $ hg help -e dodo
898 dodo extension -
902 dodo extension -
899
903
900 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
904 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
901
905
902 list of commands:
906 list of commands:
903
907
904 dodo Does nothing
908 dodo Does nothing
905 foofoo Writes 'Foo foo'
909 foofoo Writes 'Foo foo'
906
910
907 (use 'hg help -v -e dodo' to show built-in aliases and global options)
911 (use 'hg help -v -e dodo' to show built-in aliases and global options)
908
912
909 Make sure that '-v -e' prints list of built-in aliases along with
913 Make sure that '-v -e' prints list of built-in aliases along with
910 extension help itself
914 extension help itself
911 $ hg help -v -e dodo
915 $ hg help -v -e dodo
912 dodo extension -
916 dodo extension -
913
917
914 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
918 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
915
919
916 list of commands:
920 list of commands:
917
921
918 dodo Does nothing
922 dodo Does nothing
919 foofoo Writes 'Foo foo'
923 foofoo Writes 'Foo foo'
920
924
921 global options ([+] can be repeated):
925 global options ([+] can be repeated):
922
926
923 -R --repository REPO repository root directory or name of overlay bundle
927 -R --repository REPO repository root directory or name of overlay bundle
924 file
928 file
925 --cwd DIR change working directory
929 --cwd DIR change working directory
926 -y --noninteractive do not prompt, automatically pick the first choice for
930 -y --noninteractive do not prompt, automatically pick the first choice for
927 all prompts
931 all prompts
928 -q --quiet suppress output
932 -q --quiet suppress output
929 -v --verbose enable additional output
933 -v --verbose enable additional output
930 --color TYPE when to colorize (boolean, always, auto, never, or
934 --color TYPE when to colorize (boolean, always, auto, never, or
931 debug)
935 debug)
932 --config CONFIG [+] set/override config option (use 'section.name=value')
936 --config CONFIG [+] set/override config option (use 'section.name=value')
933 --debug enable debugging output
937 --debug enable debugging output
934 --debugger start debugger
938 --debugger start debugger
935 --encoding ENCODE set the charset encoding (default: ascii)
939 --encoding ENCODE set the charset encoding (default: ascii)
936 --encodingmode MODE set the charset encoding mode (default: strict)
940 --encodingmode MODE set the charset encoding mode (default: strict)
937 --traceback always print a traceback on exception
941 --traceback always print a traceback on exception
938 --time time how long the command takes
942 --time time how long the command takes
939 --profile print command execution profile
943 --profile print command execution profile
940 --version output version information and exit
944 --version output version information and exit
941 -h --help display help and exit
945 -h --help display help and exit
942 --hidden consider hidden changesets
946 --hidden consider hidden changesets
943 --pager TYPE when to paginate (boolean, always, auto, or never)
947 --pager TYPE when to paginate (boolean, always, auto, or never)
944 (default: auto)
948 (default: auto)
945
949
946 Make sure that single '-v' option shows help and built-ins only for 'dodo' command
950 Make sure that single '-v' option shows help and built-ins only for 'dodo' command
947 $ hg help -v dodo
951 $ hg help -v dodo
948 hg dodo
952 hg dodo
949
953
950 Does nothing
954 Does nothing
951
955
952 (use 'hg help -e dodo' to show help for the dodo extension)
956 (use 'hg help -e dodo' to show help for the dodo extension)
953
957
954 options:
958 options:
955
959
956 --mq operate on patch repository
960 --mq operate on patch repository
957
961
958 global options ([+] can be repeated):
962 global options ([+] can be repeated):
959
963
960 -R --repository REPO repository root directory or name of overlay bundle
964 -R --repository REPO repository root directory or name of overlay bundle
961 file
965 file
962 --cwd DIR change working directory
966 --cwd DIR change working directory
963 -y --noninteractive do not prompt, automatically pick the first choice for
967 -y --noninteractive do not prompt, automatically pick the first choice for
964 all prompts
968 all prompts
965 -q --quiet suppress output
969 -q --quiet suppress output
966 -v --verbose enable additional output
970 -v --verbose enable additional output
967 --color TYPE when to colorize (boolean, always, auto, never, or
971 --color TYPE when to colorize (boolean, always, auto, never, or
968 debug)
972 debug)
969 --config CONFIG [+] set/override config option (use 'section.name=value')
973 --config CONFIG [+] set/override config option (use 'section.name=value')
970 --debug enable debugging output
974 --debug enable debugging output
971 --debugger start debugger
975 --debugger start debugger
972 --encoding ENCODE set the charset encoding (default: ascii)
976 --encoding ENCODE set the charset encoding (default: ascii)
973 --encodingmode MODE set the charset encoding mode (default: strict)
977 --encodingmode MODE set the charset encoding mode (default: strict)
974 --traceback always print a traceback on exception
978 --traceback always print a traceback on exception
975 --time time how long the command takes
979 --time time how long the command takes
976 --profile print command execution profile
980 --profile print command execution profile
977 --version output version information and exit
981 --version output version information and exit
978 -h --help display help and exit
982 -h --help display help and exit
979 --hidden consider hidden changesets
983 --hidden consider hidden changesets
980 --pager TYPE when to paginate (boolean, always, auto, or never)
984 --pager TYPE when to paginate (boolean, always, auto, or never)
981 (default: auto)
985 (default: auto)
982
986
983 In case when extension name doesn't match any of its commands,
987 In case when extension name doesn't match any of its commands,
984 help message should ask for '-v' to get list of built-in aliases
988 help message should ask for '-v' to get list of built-in aliases
985 along with extension help
989 along with extension help
986 $ cat > $TESTTMP/d/dudu.py <<EOF
990 $ cat > $TESTTMP/d/dudu.py <<EOF
987 > """
991 > """
988 > This is an awesome 'dudu' extension. It does something and
992 > This is an awesome 'dudu' extension. It does something and
989 > also writes 'Beep beep'
993 > also writes 'Beep beep'
990 > """
994 > """
991 > from mercurial import commands, registrar
995 > from mercurial import commands, registrar
992 > cmdtable = {}
996 > cmdtable = {}
993 > command = registrar.command(cmdtable)
997 > command = registrar.command(cmdtable)
994 > @command(b'something', [], b'hg something')
998 > @command(b'something', [], b'hg something')
995 > def something(ui, *args, **kwargs):
999 > def something(ui, *args, **kwargs):
996 > """Does something"""
1000 > """Does something"""
997 > ui.write(b"I do something. Yaaay\\n")
1001 > ui.write(b"I do something. Yaaay\\n")
998 > @command(b'beep', [], b'hg beep')
1002 > @command(b'beep', [], b'hg beep')
999 > def beep(ui, *args, **kwargs):
1003 > def beep(ui, *args, **kwargs):
1000 > """Writes 'Beep beep'"""
1004 > """Writes 'Beep beep'"""
1001 > ui.write(b"Beep beep\\n")
1005 > ui.write(b"Beep beep\\n")
1002 > EOF
1006 > EOF
1003 $ dudupath=$TESTTMP/d/dudu.py
1007 $ dudupath=$TESTTMP/d/dudu.py
1004
1008
1005 $ echo "dudu = $dudupath" >> $HGRCPATH
1009 $ echo "dudu = $dudupath" >> $HGRCPATH
1006
1010
1007 $ hg help -e dudu
1011 $ hg help -e dudu
1008 dudu extension -
1012 dudu extension -
1009
1013
1010 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1014 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1011 beep'
1015 beep'
1012
1016
1013 list of commands:
1017 list of commands:
1014
1018
1015 beep Writes 'Beep beep'
1019 beep Writes 'Beep beep'
1016 something Does something
1020 something Does something
1017
1021
1018 (use 'hg help -v dudu' to show built-in aliases and global options)
1022 (use 'hg help -v dudu' to show built-in aliases and global options)
1019
1023
1020 In case when extension name doesn't match any of its commands,
1024 In case when extension name doesn't match any of its commands,
1021 help options '-v' and '-v -e' should be equivalent
1025 help options '-v' and '-v -e' should be equivalent
1022 $ hg help -v dudu
1026 $ hg help -v dudu
1023 dudu extension -
1027 dudu extension -
1024
1028
1025 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1029 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1026 beep'
1030 beep'
1027
1031
1028 list of commands:
1032 list of commands:
1029
1033
1030 beep Writes 'Beep beep'
1034 beep Writes 'Beep beep'
1031 something Does something
1035 something Does something
1032
1036
1033 global options ([+] can be repeated):
1037 global options ([+] can be repeated):
1034
1038
1035 -R --repository REPO repository root directory or name of overlay bundle
1039 -R --repository REPO repository root directory or name of overlay bundle
1036 file
1040 file
1037 --cwd DIR change working directory
1041 --cwd DIR change working directory
1038 -y --noninteractive do not prompt, automatically pick the first choice for
1042 -y --noninteractive do not prompt, automatically pick the first choice for
1039 all prompts
1043 all prompts
1040 -q --quiet suppress output
1044 -q --quiet suppress output
1041 -v --verbose enable additional output
1045 -v --verbose enable additional output
1042 --color TYPE when to colorize (boolean, always, auto, never, or
1046 --color TYPE when to colorize (boolean, always, auto, never, or
1043 debug)
1047 debug)
1044 --config CONFIG [+] set/override config option (use 'section.name=value')
1048 --config CONFIG [+] set/override config option (use 'section.name=value')
1045 --debug enable debugging output
1049 --debug enable debugging output
1046 --debugger start debugger
1050 --debugger start debugger
1047 --encoding ENCODE set the charset encoding (default: ascii)
1051 --encoding ENCODE set the charset encoding (default: ascii)
1048 --encodingmode MODE set the charset encoding mode (default: strict)
1052 --encodingmode MODE set the charset encoding mode (default: strict)
1049 --traceback always print a traceback on exception
1053 --traceback always print a traceback on exception
1050 --time time how long the command takes
1054 --time time how long the command takes
1051 --profile print command execution profile
1055 --profile print command execution profile
1052 --version output version information and exit
1056 --version output version information and exit
1053 -h --help display help and exit
1057 -h --help display help and exit
1054 --hidden consider hidden changesets
1058 --hidden consider hidden changesets
1055 --pager TYPE when to paginate (boolean, always, auto, or never)
1059 --pager TYPE when to paginate (boolean, always, auto, or never)
1056 (default: auto)
1060 (default: auto)
1057
1061
1058 $ hg help -v -e dudu
1062 $ hg help -v -e dudu
1059 dudu extension -
1063 dudu extension -
1060
1064
1061 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1065 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1062 beep'
1066 beep'
1063
1067
1064 list of commands:
1068 list of commands:
1065
1069
1066 beep Writes 'Beep beep'
1070 beep Writes 'Beep beep'
1067 something Does something
1071 something Does something
1068
1072
1069 global options ([+] can be repeated):
1073 global options ([+] can be repeated):
1070
1074
1071 -R --repository REPO repository root directory or name of overlay bundle
1075 -R --repository REPO repository root directory or name of overlay bundle
1072 file
1076 file
1073 --cwd DIR change working directory
1077 --cwd DIR change working directory
1074 -y --noninteractive do not prompt, automatically pick the first choice for
1078 -y --noninteractive do not prompt, automatically pick the first choice for
1075 all prompts
1079 all prompts
1076 -q --quiet suppress output
1080 -q --quiet suppress output
1077 -v --verbose enable additional output
1081 -v --verbose enable additional output
1078 --color TYPE when to colorize (boolean, always, auto, never, or
1082 --color TYPE when to colorize (boolean, always, auto, never, or
1079 debug)
1083 debug)
1080 --config CONFIG [+] set/override config option (use 'section.name=value')
1084 --config CONFIG [+] set/override config option (use 'section.name=value')
1081 --debug enable debugging output
1085 --debug enable debugging output
1082 --debugger start debugger
1086 --debugger start debugger
1083 --encoding ENCODE set the charset encoding (default: ascii)
1087 --encoding ENCODE set the charset encoding (default: ascii)
1084 --encodingmode MODE set the charset encoding mode (default: strict)
1088 --encodingmode MODE set the charset encoding mode (default: strict)
1085 --traceback always print a traceback on exception
1089 --traceback always print a traceback on exception
1086 --time time how long the command takes
1090 --time time how long the command takes
1087 --profile print command execution profile
1091 --profile print command execution profile
1088 --version output version information and exit
1092 --version output version information and exit
1089 -h --help display help and exit
1093 -h --help display help and exit
1090 --hidden consider hidden changesets
1094 --hidden consider hidden changesets
1091 --pager TYPE when to paginate (boolean, always, auto, or never)
1095 --pager TYPE when to paginate (boolean, always, auto, or never)
1092 (default: auto)
1096 (default: auto)
1093
1097
1094 Disabled extension commands:
1098 Disabled extension commands:
1095
1099
1096 $ ORGHGRCPATH=$HGRCPATH
1100 $ ORGHGRCPATH=$HGRCPATH
1097 $ HGRCPATH=
1101 $ HGRCPATH=
1098 $ export HGRCPATH
1102 $ export HGRCPATH
1099 $ hg help email
1103 $ hg help email
1100 'email' is provided by the following extension:
1104 'email' is provided by the following extension:
1101
1105
1102 patchbomb command to send changesets as (a series of) patch emails
1106 patchbomb command to send changesets as (a series of) patch emails
1103
1107
1104 (use 'hg help extensions' for information on enabling extensions)
1108 (use 'hg help extensions' for information on enabling extensions)
1105
1109
1106
1110
1107 $ hg qdel
1111 $ hg qdel
1108 hg: unknown command 'qdel'
1112 hg: unknown command 'qdel'
1109 'qdelete' is provided by the following extension:
1113 'qdelete' is provided by the following extension:
1110
1114
1111 mq manage a stack of patches
1115 mq manage a stack of patches
1112
1116
1113 (use 'hg help extensions' for information on enabling extensions)
1117 (use 'hg help extensions' for information on enabling extensions)
1114 [255]
1118 [255]
1115
1119
1116
1120
1117 $ hg churn
1121 $ hg churn
1118 hg: unknown command 'churn'
1122 hg: unknown command 'churn'
1119 'churn' is provided by the following extension:
1123 'churn' is provided by the following extension:
1120
1124
1121 churn command to display statistics about repository history
1125 churn command to display statistics about repository history
1122
1126
1123 (use 'hg help extensions' for information on enabling extensions)
1127 (use 'hg help extensions' for information on enabling extensions)
1124 [255]
1128 [255]
1125
1129
1126
1130
1127
1131
1128 Disabled extensions:
1132 Disabled extensions:
1129
1133
1130 $ hg help churn
1134 $ hg help churn
1131 churn extension - command to display statistics about repository history
1135 churn extension - command to display statistics about repository history
1132
1136
1133 (use 'hg help extensions' for information on enabling extensions)
1137 (use 'hg help extensions' for information on enabling extensions)
1134
1138
1135 $ hg help patchbomb
1139 $ hg help patchbomb
1136 patchbomb extension - command to send changesets as (a series of) patch emails
1140 patchbomb extension - command to send changesets as (a series of) patch emails
1137
1141
1138 The series is started off with a "[PATCH 0 of N]" introduction, which
1142 The series is started off with a "[PATCH 0 of N]" introduction, which
1139 describes the series as a whole.
1143 describes the series as a whole.
1140
1144
1141 Each patch email has a Subject line of "[PATCH M of N] ...", using the first
1145 Each patch email has a Subject line of "[PATCH M of N] ...", using the first
1142 line of the changeset description as the subject text. The message contains
1146 line of the changeset description as the subject text. The message contains
1143 two or three body parts:
1147 two or three body parts:
1144
1148
1145 - The changeset description.
1149 - The changeset description.
1146 - [Optional] The result of running diffstat on the patch.
1150 - [Optional] The result of running diffstat on the patch.
1147 - The patch itself, as generated by 'hg export'.
1151 - The patch itself, as generated by 'hg export'.
1148
1152
1149 Each message refers to the first in the series using the In-Reply-To and
1153 Each message refers to the first in the series using the In-Reply-To and
1150 References headers, so they will show up as a sequence in threaded mail and
1154 References headers, so they will show up as a sequence in threaded mail and
1151 news readers, and in mail archives.
1155 news readers, and in mail archives.
1152
1156
1153 To configure other defaults, add a section like this to your configuration
1157 To configure other defaults, add a section like this to your configuration
1154 file:
1158 file:
1155
1159
1156 [email]
1160 [email]
1157 from = My Name <my@email>
1161 from = My Name <my@email>
1158 to = recipient1, recipient2, ...
1162 to = recipient1, recipient2, ...
1159 cc = cc1, cc2, ...
1163 cc = cc1, cc2, ...
1160 bcc = bcc1, bcc2, ...
1164 bcc = bcc1, bcc2, ...
1161 reply-to = address1, address2, ...
1165 reply-to = address1, address2, ...
1162
1166
1163 Use "[patchbomb]" as configuration section name if you need to override global
1167 Use "[patchbomb]" as configuration section name if you need to override global
1164 "[email]" address settings.
1168 "[email]" address settings.
1165
1169
1166 Then you can use the 'hg email' command to mail a series of changesets as a
1170 Then you can use the 'hg email' command to mail a series of changesets as a
1167 patchbomb.
1171 patchbomb.
1168
1172
1169 You can also either configure the method option in the email section to be a
1173 You can also either configure the method option in the email section to be a
1170 sendmail compatible mailer or fill out the [smtp] section so that the
1174 sendmail compatible mailer or fill out the [smtp] section so that the
1171 patchbomb extension can automatically send patchbombs directly from the
1175 patchbomb extension can automatically send patchbombs directly from the
1172 commandline. See the [email] and [smtp] sections in hgrc(5) for details.
1176 commandline. See the [email] and [smtp] sections in hgrc(5) for details.
1173
1177
1174 By default, 'hg email' will prompt for a "To" or "CC" header if you do not
1178 By default, 'hg email' will prompt for a "To" or "CC" header if you do not
1175 supply one via configuration or the command line. You can override this to
1179 supply one via configuration or the command line. You can override this to
1176 never prompt by configuring an empty value:
1180 never prompt by configuring an empty value:
1177
1181
1178 [email]
1182 [email]
1179 cc =
1183 cc =
1180
1184
1181 You can control the default inclusion of an introduction message with the
1185 You can control the default inclusion of an introduction message with the
1182 "patchbomb.intro" configuration option. The configuration is always
1186 "patchbomb.intro" configuration option. The configuration is always
1183 overwritten by command line flags like --intro and --desc:
1187 overwritten by command line flags like --intro and --desc:
1184
1188
1185 [patchbomb]
1189 [patchbomb]
1186 intro=auto # include introduction message if more than 1 patch (default)
1190 intro=auto # include introduction message if more than 1 patch (default)
1187 intro=never # never include an introduction message
1191 intro=never # never include an introduction message
1188 intro=always # always include an introduction message
1192 intro=always # always include an introduction message
1189
1193
1190 You can specify a template for flags to be added in subject prefixes. Flags
1194 You can specify a template for flags to be added in subject prefixes. Flags
1191 specified by --flag option are exported as "{flags}" keyword:
1195 specified by --flag option are exported as "{flags}" keyword:
1192
1196
1193 [patchbomb]
1197 [patchbomb]
1194 flagtemplate = "{separate(' ',
1198 flagtemplate = "{separate(' ',
1195 ifeq(branch, 'default', '', branch|upper),
1199 ifeq(branch, 'default', '', branch|upper),
1196 flags)}"
1200 flags)}"
1197
1201
1198 You can set patchbomb to always ask for confirmation by setting
1202 You can set patchbomb to always ask for confirmation by setting
1199 "patchbomb.confirm" to true.
1203 "patchbomb.confirm" to true.
1200
1204
1201 (use 'hg help extensions' for information on enabling extensions)
1205 (use 'hg help extensions' for information on enabling extensions)
1202
1206
1203
1207
1204 Broken disabled extension and command:
1208 Broken disabled extension and command:
1205
1209
1206 $ mkdir hgext
1210 $ mkdir hgext
1207 $ echo > hgext/__init__.py
1211 $ echo > hgext/__init__.py
1208 $ cat > hgext/broken.py <<EOF
1212 $ cat > hgext/broken.py <<EOF
1209 > "broken extension'
1213 > "broken extension'
1210 > EOF
1214 > EOF
1211 $ cat > path.py <<EOF
1215 $ cat > path.py <<EOF
1212 > import os, sys
1216 > import os, sys
1213 > sys.path.insert(0, os.environ['HGEXTPATH'])
1217 > sys.path.insert(0, os.environ['HGEXTPATH'])
1214 > EOF
1218 > EOF
1215 $ HGEXTPATH=`pwd`
1219 $ HGEXTPATH=`pwd`
1216 $ export HGEXTPATH
1220 $ export HGEXTPATH
1217
1221
1218 $ hg --config extensions.path=./path.py help broken
1222 $ hg --config extensions.path=./path.py help broken
1219 broken extension - (no help text available)
1223 broken extension - (no help text available)
1220
1224
1221 (use 'hg help extensions' for information on enabling extensions)
1225 (use 'hg help extensions' for information on enabling extensions)
1222
1226
1223
1227
1224 $ cat > hgext/forest.py <<EOF
1228 $ cat > hgext/forest.py <<EOF
1225 > cmdtable = None
1229 > cmdtable = None
1226 > EOF
1230 > EOF
1227 $ hg --config extensions.path=./path.py help foo > /dev/null
1231 $ hg --config extensions.path=./path.py help foo > /dev/null
1228 warning: error finding commands in $TESTTMP/hgext/forest.py
1232 warning: error finding commands in $TESTTMP/hgext/forest.py
1229 abort: no such help topic: foo
1233 abort: no such help topic: foo
1230 (try 'hg help --keyword foo')
1234 (try 'hg help --keyword foo')
1231 [255]
1235 [255]
1232
1236
1233 $ cat > throw.py <<EOF
1237 $ cat > throw.py <<EOF
1234 > from mercurial import commands, registrar, util
1238 > from mercurial import commands, registrar, util
1235 > cmdtable = {}
1239 > cmdtable = {}
1236 > command = registrar.command(cmdtable)
1240 > command = registrar.command(cmdtable)
1237 > class Bogon(Exception): pass
1241 > class Bogon(Exception): pass
1238 > @command(b'throw', [], b'hg throw', norepo=True)
1242 > @command(b'throw', [], b'hg throw', norepo=True)
1239 > def throw(ui, **opts):
1243 > def throw(ui, **opts):
1240 > """throws an exception"""
1244 > """throws an exception"""
1241 > raise Bogon()
1245 > raise Bogon()
1242 > EOF
1246 > EOF
1243
1247
1244 No declared supported version, extension complains:
1248 No declared supported version, extension complains:
1245 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1249 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1246 ** Unknown exception encountered with possibly-broken third-party extension throw
1250 ** Unknown exception encountered with possibly-broken third-party extension throw
1247 ** which supports versions unknown of Mercurial.
1251 ** which supports versions unknown of Mercurial.
1248 ** Please disable throw and try your action again.
1252 ** Please disable throw and try your action again.
1249 ** If that fixes the bug please report it to the extension author.
1253 ** If that fixes the bug please report it to the extension author.
1250 ** Python * (glob)
1254 ** Python * (glob)
1251 ** Mercurial Distributed SCM * (glob)
1255 ** Mercurial Distributed SCM * (glob)
1252 ** Extensions loaded: throw
1256 ** Extensions loaded: throw
1253
1257
1254 empty declaration of supported version, extension complains:
1258 empty declaration of supported version, extension complains:
1255 $ echo "testedwith = ''" >> throw.py
1259 $ echo "testedwith = ''" >> throw.py
1256 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1260 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1257 ** Unknown exception encountered with possibly-broken third-party extension throw
1261 ** Unknown exception encountered with possibly-broken third-party extension throw
1258 ** which supports versions unknown of Mercurial.
1262 ** which supports versions unknown of Mercurial.
1259 ** Please disable throw and try your action again.
1263 ** Please disable throw and try your action again.
1260 ** If that fixes the bug please report it to the extension author.
1264 ** If that fixes the bug please report it to the extension author.
1261 ** Python * (glob)
1265 ** Python * (glob)
1262 ** Mercurial Distributed SCM (*) (glob)
1266 ** Mercurial Distributed SCM (*) (glob)
1263 ** Extensions loaded: throw
1267 ** Extensions loaded: throw
1264
1268
1265 If the extension specifies a buglink, show that:
1269 If the extension specifies a buglink, show that:
1266 $ echo 'buglink = "http://example.com/bts"' >> throw.py
1270 $ echo 'buglink = "http://example.com/bts"' >> throw.py
1267 $ rm -f throw.pyc throw.pyo
1271 $ rm -f throw.pyc throw.pyo
1268 $ rm -Rf __pycache__
1272 $ rm -Rf __pycache__
1269 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1273 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1270 ** Unknown exception encountered with possibly-broken third-party extension throw
1274 ** Unknown exception encountered with possibly-broken third-party extension throw
1271 ** which supports versions unknown of Mercurial.
1275 ** which supports versions unknown of Mercurial.
1272 ** Please disable throw and try your action again.
1276 ** Please disable throw and try your action again.
1273 ** If that fixes the bug please report it to http://example.com/bts
1277 ** If that fixes the bug please report it to http://example.com/bts
1274 ** Python * (glob)
1278 ** Python * (glob)
1275 ** Mercurial Distributed SCM (*) (glob)
1279 ** Mercurial Distributed SCM (*) (glob)
1276 ** Extensions loaded: throw
1280 ** Extensions loaded: throw
1277
1281
1278 If the extensions declare outdated versions, accuse the older extension first:
1282 If the extensions declare outdated versions, accuse the older extension first:
1279 $ echo "from mercurial import util" >> older.py
1283 $ echo "from mercurial import util" >> older.py
1280 $ echo "util.version = lambda:'2.2'" >> older.py
1284 $ echo "util.version = lambda:'2.2'" >> older.py
1281 $ echo "testedwith = b'1.9.3'" >> older.py
1285 $ echo "testedwith = b'1.9.3'" >> older.py
1282 $ echo "testedwith = b'2.1.1'" >> throw.py
1286 $ echo "testedwith = b'2.1.1'" >> throw.py
1283 $ rm -f throw.pyc throw.pyo
1287 $ rm -f throw.pyc throw.pyo
1284 $ rm -Rf __pycache__
1288 $ rm -Rf __pycache__
1285 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1289 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1286 > throw 2>&1 | egrep '^\*\*'
1290 > throw 2>&1 | egrep '^\*\*'
1287 ** Unknown exception encountered with possibly-broken third-party extension older
1291 ** Unknown exception encountered with possibly-broken third-party extension older
1288 ** which supports versions 1.9 of Mercurial.
1292 ** which supports versions 1.9 of Mercurial.
1289 ** Please disable older and try your action again.
1293 ** Please disable older and try your action again.
1290 ** If that fixes the bug please report it to the extension author.
1294 ** If that fixes the bug please report it to the extension author.
1291 ** Python * (glob)
1295 ** Python * (glob)
1292 ** Mercurial Distributed SCM (version 2.2)
1296 ** Mercurial Distributed SCM (version 2.2)
1293 ** Extensions loaded: throw, older
1297 ** Extensions loaded: throw, older
1294
1298
1295 One extension only tested with older, one only with newer versions:
1299 One extension only tested with older, one only with newer versions:
1296 $ echo "util.version = lambda:b'2.1'" >> older.py
1300 $ echo "util.version = lambda:b'2.1'" >> older.py
1297 $ rm -f older.pyc older.pyo
1301 $ rm -f older.pyc older.pyo
1298 $ rm -Rf __pycache__
1302 $ rm -Rf __pycache__
1299 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1303 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1300 > throw 2>&1 | egrep '^\*\*'
1304 > throw 2>&1 | egrep '^\*\*'
1301 ** Unknown exception encountered with possibly-broken third-party extension older
1305 ** Unknown exception encountered with possibly-broken third-party extension older
1302 ** which supports versions 1.9 of Mercurial.
1306 ** which supports versions 1.9 of Mercurial.
1303 ** Please disable older and try your action again.
1307 ** Please disable older and try your action again.
1304 ** If that fixes the bug please report it to the extension author.
1308 ** If that fixes the bug please report it to the extension author.
1305 ** Python * (glob)
1309 ** Python * (glob)
1306 ** Mercurial Distributed SCM (version 2.1)
1310 ** Mercurial Distributed SCM (version 2.1)
1307 ** Extensions loaded: throw, older
1311 ** Extensions loaded: throw, older
1308
1312
1309 Older extension is tested with current version, the other only with newer:
1313 Older extension is tested with current version, the other only with newer:
1310 $ echo "util.version = lambda:b'1.9.3'" >> older.py
1314 $ echo "util.version = lambda:b'1.9.3'" >> older.py
1311 $ rm -f older.pyc older.pyo
1315 $ rm -f older.pyc older.pyo
1312 $ rm -Rf __pycache__
1316 $ rm -Rf __pycache__
1313 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1317 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1314 > throw 2>&1 | egrep '^\*\*'
1318 > throw 2>&1 | egrep '^\*\*'
1315 ** Unknown exception encountered with possibly-broken third-party extension throw
1319 ** Unknown exception encountered with possibly-broken third-party extension throw
1316 ** which supports versions 2.1 of Mercurial.
1320 ** which supports versions 2.1 of Mercurial.
1317 ** Please disable throw and try your action again.
1321 ** Please disable throw and try your action again.
1318 ** If that fixes the bug please report it to http://example.com/bts
1322 ** If that fixes the bug please report it to http://example.com/bts
1319 ** Python * (glob)
1323 ** Python * (glob)
1320 ** Mercurial Distributed SCM (version 1.9.3)
1324 ** Mercurial Distributed SCM (version 1.9.3)
1321 ** Extensions loaded: throw, older
1325 ** Extensions loaded: throw, older
1322
1326
1323 Ability to point to a different point
1327 Ability to point to a different point
1324 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1328 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1325 > --config ui.supportcontact='Your Local Goat Lenders' throw 2>&1 | egrep '^\*\*'
1329 > --config ui.supportcontact='Your Local Goat Lenders' throw 2>&1 | egrep '^\*\*'
1326 ** unknown exception encountered, please report by visiting
1330 ** unknown exception encountered, please report by visiting
1327 ** Your Local Goat Lenders
1331 ** Your Local Goat Lenders
1328 ** Python * (glob)
1332 ** Python * (glob)
1329 ** Mercurial Distributed SCM (*) (glob)
1333 ** Mercurial Distributed SCM (*) (glob)
1330 ** Extensions loaded: throw, older
1334 ** Extensions loaded: throw, older
1331
1335
1332 Declare the version as supporting this hg version, show regular bts link:
1336 Declare the version as supporting this hg version, show regular bts link:
1333 $ hgver=`hg debuginstall -T '{hgver}'`
1337 $ hgver=`hg debuginstall -T '{hgver}'`
1334 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
1338 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
1335 $ if [ -z "$hgver" ]; then
1339 $ if [ -z "$hgver" ]; then
1336 > echo "unable to fetch a mercurial version. Make sure __version__ is correct";
1340 > echo "unable to fetch a mercurial version. Make sure __version__ is correct";
1337 > fi
1341 > fi
1338 $ rm -f throw.pyc throw.pyo
1342 $ rm -f throw.pyc throw.pyo
1339 $ rm -Rf __pycache__
1343 $ rm -Rf __pycache__
1340 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1344 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1341 ** unknown exception encountered, please report by visiting
1345 ** unknown exception encountered, please report by visiting
1342 ** https://mercurial-scm.org/wiki/BugTracker
1346 ** https://mercurial-scm.org/wiki/BugTracker
1343 ** Python * (glob)
1347 ** Python * (glob)
1344 ** Mercurial Distributed SCM (*) (glob)
1348 ** Mercurial Distributed SCM (*) (glob)
1345 ** Extensions loaded: throw
1349 ** Extensions loaded: throw
1346
1350
1347 Patch version is ignored during compatibility check
1351 Patch version is ignored during compatibility check
1348 $ echo "testedwith = b'3.2'" >> throw.py
1352 $ echo "testedwith = b'3.2'" >> throw.py
1349 $ echo "util.version = lambda:b'3.2.2'" >> throw.py
1353 $ echo "util.version = lambda:b'3.2.2'" >> throw.py
1350 $ rm -f throw.pyc throw.pyo
1354 $ rm -f throw.pyc throw.pyo
1351 $ rm -Rf __pycache__
1355 $ rm -Rf __pycache__
1352 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1356 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1353 ** unknown exception encountered, please report by visiting
1357 ** unknown exception encountered, please report by visiting
1354 ** https://mercurial-scm.org/wiki/BugTracker
1358 ** https://mercurial-scm.org/wiki/BugTracker
1355 ** Python * (glob)
1359 ** Python * (glob)
1356 ** Mercurial Distributed SCM (*) (glob)
1360 ** Mercurial Distributed SCM (*) (glob)
1357 ** Extensions loaded: throw
1361 ** Extensions loaded: throw
1358
1362
1359 Test version number support in 'hg version':
1363 Test version number support in 'hg version':
1360 $ echo '__version__ = (1, 2, 3)' >> throw.py
1364 $ echo '__version__ = (1, 2, 3)' >> throw.py
1361 $ rm -f throw.pyc throw.pyo
1365 $ rm -f throw.pyc throw.pyo
1362 $ rm -Rf __pycache__
1366 $ rm -Rf __pycache__
1363 $ hg version -v
1367 $ hg version -v
1364 Mercurial Distributed SCM (version *) (glob)
1368 Mercurial Distributed SCM (version *) (glob)
1365 (see https://mercurial-scm.org for more information)
1369 (see https://mercurial-scm.org for more information)
1366
1370
1367 Copyright (C) 2005-* Matt Mackall and others (glob)
1371 Copyright (C) 2005-* Matt Mackall and others (glob)
1368 This is free software; see the source for copying conditions. There is NO
1372 This is free software; see the source for copying conditions. There is NO
1369 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1373 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1370
1374
1371 Enabled extensions:
1375 Enabled extensions:
1372
1376
1373
1377
1374 $ hg version -v --config extensions.throw=throw.py
1378 $ hg version -v --config extensions.throw=throw.py
1375 Mercurial Distributed SCM (version *) (glob)
1379 Mercurial Distributed SCM (version *) (glob)
1376 (see https://mercurial-scm.org for more information)
1380 (see https://mercurial-scm.org for more information)
1377
1381
1378 Copyright (C) 2005-* Matt Mackall and others (glob)
1382 Copyright (C) 2005-* Matt Mackall and others (glob)
1379 This is free software; see the source for copying conditions. There is NO
1383 This is free software; see the source for copying conditions. There is NO
1380 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1384 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1381
1385
1382 Enabled extensions:
1386 Enabled extensions:
1383
1387
1384 throw external 1.2.3
1388 throw external 1.2.3
1385 $ echo 'getversion = lambda: "1.twentythree"' >> throw.py
1389 $ echo 'getversion = lambda: "1.twentythree"' >> throw.py
1386 $ rm -f throw.pyc throw.pyo
1390 $ rm -f throw.pyc throw.pyo
1387 $ rm -Rf __pycache__
1391 $ rm -Rf __pycache__
1388 $ hg version -v --config extensions.throw=throw.py --config extensions.strip=
1392 $ hg version -v --config extensions.throw=throw.py --config extensions.strip=
1389 Mercurial Distributed SCM (version *) (glob)
1393 Mercurial Distributed SCM (version *) (glob)
1390 (see https://mercurial-scm.org for more information)
1394 (see https://mercurial-scm.org for more information)
1391
1395
1392 Copyright (C) 2005-* Matt Mackall and others (glob)
1396 Copyright (C) 2005-* Matt Mackall and others (glob)
1393 This is free software; see the source for copying conditions. There is NO
1397 This is free software; see the source for copying conditions. There is NO
1394 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1398 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1395
1399
1396 Enabled extensions:
1400 Enabled extensions:
1397
1401
1398 throw external 1.twentythree
1402 throw external 1.twentythree
1399 strip internal
1403 strip internal
1400
1404
1401 $ hg version -q --config extensions.throw=throw.py
1405 $ hg version -q --config extensions.throw=throw.py
1402 Mercurial Distributed SCM (version *) (glob)
1406 Mercurial Distributed SCM (version *) (glob)
1403
1407
1404 Test JSON output of version:
1408 Test JSON output of version:
1405
1409
1406 $ hg version -Tjson
1410 $ hg version -Tjson
1407 [
1411 [
1408 {
1412 {
1409 "extensions": [],
1413 "extensions": [],
1410 "ver": "*" (glob)
1414 "ver": "*" (glob)
1411 }
1415 }
1412 ]
1416 ]
1413
1417
1414 $ hg version --config extensions.throw=throw.py -Tjson
1418 $ hg version --config extensions.throw=throw.py -Tjson
1415 [
1419 [
1416 {
1420 {
1417 "extensions": [{"bundled": false, "name": "throw", "ver": "1.twentythree"}],
1421 "extensions": [{"bundled": false, "name": "throw", "ver": "1.twentythree"}],
1418 "ver": "3.2.2"
1422 "ver": "3.2.2"
1419 }
1423 }
1420 ]
1424 ]
1421
1425
1422 $ hg version --config extensions.strip= -Tjson
1426 $ hg version --config extensions.strip= -Tjson
1423 [
1427 [
1424 {
1428 {
1425 "extensions": [{"bundled": true, "name": "strip", "ver": null}],
1429 "extensions": [{"bundled": true, "name": "strip", "ver": null}],
1426 "ver": "*" (glob)
1430 "ver": "*" (glob)
1427 }
1431 }
1428 ]
1432 ]
1429
1433
1430 Test template output of version:
1434 Test template output of version:
1431
1435
1432 $ hg version --config extensions.throw=throw.py --config extensions.strip= \
1436 $ hg version --config extensions.throw=throw.py --config extensions.strip= \
1433 > -T'{extensions % "{name} {pad(ver, 16)} ({if(bundled, "internal", "external")})\n"}'
1437 > -T'{extensions % "{name} {pad(ver, 16)} ({if(bundled, "internal", "external")})\n"}'
1434 throw 1.twentythree (external)
1438 throw 1.twentythree (external)
1435 strip (internal)
1439 strip (internal)
1436
1440
1437 Refuse to load extensions with minimum version requirements
1441 Refuse to load extensions with minimum version requirements
1438
1442
1439 $ cat > minversion1.py << EOF
1443 $ cat > minversion1.py << EOF
1440 > from mercurial import util
1444 > from mercurial import util
1441 > util.version = lambda: b'3.5.2'
1445 > util.version = lambda: b'3.5.2'
1442 > minimumhgversion = b'3.6'
1446 > minimumhgversion = b'3.6'
1443 > EOF
1447 > EOF
1444 $ hg --config extensions.minversion=minversion1.py version
1448 $ hg --config extensions.minversion=minversion1.py version
1445 (third party extension minversion requires version 3.6 or newer of Mercurial; disabling)
1449 (third party extension minversion requires version 3.6 or newer of Mercurial; disabling)
1446 Mercurial Distributed SCM (version 3.5.2)
1450 Mercurial Distributed SCM (version 3.5.2)
1447 (see https://mercurial-scm.org for more information)
1451 (see https://mercurial-scm.org for more information)
1448
1452
1449 Copyright (C) 2005-* Matt Mackall and others (glob)
1453 Copyright (C) 2005-* Matt Mackall and others (glob)
1450 This is free software; see the source for copying conditions. There is NO
1454 This is free software; see the source for copying conditions. There is NO
1451 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1455 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1452
1456
1453 $ cat > minversion2.py << EOF
1457 $ cat > minversion2.py << EOF
1454 > from mercurial import util
1458 > from mercurial import util
1455 > util.version = lambda: b'3.6'
1459 > util.version = lambda: b'3.6'
1456 > minimumhgversion = b'3.7'
1460 > minimumhgversion = b'3.7'
1457 > EOF
1461 > EOF
1458 $ hg --config extensions.minversion=minversion2.py version 2>&1 | egrep '\(third'
1462 $ hg --config extensions.minversion=minversion2.py version 2>&1 | egrep '\(third'
1459 (third party extension minversion requires version 3.7 or newer of Mercurial; disabling)
1463 (third party extension minversion requires version 3.7 or newer of Mercurial; disabling)
1460
1464
1461 Can load version that is only off by point release
1465 Can load version that is only off by point release
1462
1466
1463 $ cat > minversion2.py << EOF
1467 $ cat > minversion2.py << EOF
1464 > from mercurial import util
1468 > from mercurial import util
1465 > util.version = lambda: b'3.6.1'
1469 > util.version = lambda: b'3.6.1'
1466 > minimumhgversion = b'3.6'
1470 > minimumhgversion = b'3.6'
1467 > EOF
1471 > EOF
1468 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1472 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1469 [1]
1473 [1]
1470
1474
1471 Can load minimum version identical to current
1475 Can load minimum version identical to current
1472
1476
1473 $ cat > minversion3.py << EOF
1477 $ cat > minversion3.py << EOF
1474 > from mercurial import util
1478 > from mercurial import util
1475 > util.version = lambda: b'3.5'
1479 > util.version = lambda: b'3.5'
1476 > minimumhgversion = b'3.5'
1480 > minimumhgversion = b'3.5'
1477 > EOF
1481 > EOF
1478 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1482 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1479 [1]
1483 [1]
1480
1484
1481 Restore HGRCPATH
1485 Restore HGRCPATH
1482
1486
1483 $ HGRCPATH=$ORGHGRCPATH
1487 $ HGRCPATH=$ORGHGRCPATH
1484 $ export HGRCPATH
1488 $ export HGRCPATH
1485
1489
1486 Commands handling multiple repositories at a time should invoke only
1490 Commands handling multiple repositories at a time should invoke only
1487 "reposetup()" of extensions enabling in the target repository.
1491 "reposetup()" of extensions enabling in the target repository.
1488
1492
1489 $ mkdir reposetup-test
1493 $ mkdir reposetup-test
1490 $ cd reposetup-test
1494 $ cd reposetup-test
1491
1495
1492 $ cat > $TESTTMP/reposetuptest.py <<EOF
1496 $ cat > $TESTTMP/reposetuptest.py <<EOF
1493 > from mercurial import extensions
1497 > from mercurial import extensions
1494 > def reposetup(ui, repo):
1498 > def reposetup(ui, repo):
1495 > ui.write(b'reposetup() for %s\n' % (repo.root))
1499 > ui.write(b'reposetup() for %s\n' % (repo.root))
1496 > ui.flush()
1500 > ui.flush()
1497 > EOF
1501 > EOF
1498 $ hg init src
1502 $ hg init src
1499 $ echo a > src/a
1503 $ echo a > src/a
1500 $ hg -R src commit -Am '#0 at src/a'
1504 $ hg -R src commit -Am '#0 at src/a'
1501 adding a
1505 adding a
1502 $ echo '[extensions]' >> src/.hg/hgrc
1506 $ echo '[extensions]' >> src/.hg/hgrc
1503 $ echo '# enable extension locally' >> src/.hg/hgrc
1507 $ echo '# enable extension locally' >> src/.hg/hgrc
1504 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
1508 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
1505 $ hg -R src status
1509 $ hg -R src status
1506 reposetup() for $TESTTMP/reposetup-test/src
1510 reposetup() for $TESTTMP/reposetup-test/src
1507 reposetup() for $TESTTMP/reposetup-test/src (chg !)
1511 reposetup() for $TESTTMP/reposetup-test/src (chg !)
1508
1512
1509 $ hg clone -U src clone-dst1
1513 $ hg clone -U src clone-dst1
1510 reposetup() for $TESTTMP/reposetup-test/src
1514 reposetup() for $TESTTMP/reposetup-test/src
1511 $ hg init push-dst1
1515 $ hg init push-dst1
1512 $ hg -q -R src push push-dst1
1516 $ hg -q -R src push push-dst1
1513 reposetup() for $TESTTMP/reposetup-test/src
1517 reposetup() for $TESTTMP/reposetup-test/src
1514 $ hg init pull-src1
1518 $ hg init pull-src1
1515 $ hg -q -R pull-src1 pull src
1519 $ hg -q -R pull-src1 pull src
1516 reposetup() for $TESTTMP/reposetup-test/src
1520 reposetup() for $TESTTMP/reposetup-test/src
1517
1521
1518 $ cat <<EOF >> $HGRCPATH
1522 $ cat <<EOF >> $HGRCPATH
1519 > [extensions]
1523 > [extensions]
1520 > # disable extension globally and explicitly
1524 > # disable extension globally and explicitly
1521 > reposetuptest = !
1525 > reposetuptest = !
1522 > EOF
1526 > EOF
1523 $ hg clone -U src clone-dst2
1527 $ hg clone -U src clone-dst2
1524 reposetup() for $TESTTMP/reposetup-test/src
1528 reposetup() for $TESTTMP/reposetup-test/src
1525 $ hg init push-dst2
1529 $ hg init push-dst2
1526 $ hg -q -R src push push-dst2
1530 $ hg -q -R src push push-dst2
1527 reposetup() for $TESTTMP/reposetup-test/src
1531 reposetup() for $TESTTMP/reposetup-test/src
1528 $ hg init pull-src2
1532 $ hg init pull-src2
1529 $ hg -q -R pull-src2 pull src
1533 $ hg -q -R pull-src2 pull src
1530 reposetup() for $TESTTMP/reposetup-test/src
1534 reposetup() for $TESTTMP/reposetup-test/src
1531
1535
1532 $ cat <<EOF >> $HGRCPATH
1536 $ cat <<EOF >> $HGRCPATH
1533 > [extensions]
1537 > [extensions]
1534 > # enable extension globally
1538 > # enable extension globally
1535 > reposetuptest = $TESTTMP/reposetuptest.py
1539 > reposetuptest = $TESTTMP/reposetuptest.py
1536 > EOF
1540 > EOF
1537 $ hg clone -U src clone-dst3
1541 $ hg clone -U src clone-dst3
1538 reposetup() for $TESTTMP/reposetup-test/src
1542 reposetup() for $TESTTMP/reposetup-test/src
1539 reposetup() for $TESTTMP/reposetup-test/clone-dst3
1543 reposetup() for $TESTTMP/reposetup-test/clone-dst3
1540 $ hg init push-dst3
1544 $ hg init push-dst3
1541 reposetup() for $TESTTMP/reposetup-test/push-dst3
1545 reposetup() for $TESTTMP/reposetup-test/push-dst3
1542 $ hg -q -R src push push-dst3
1546 $ hg -q -R src push push-dst3
1543 reposetup() for $TESTTMP/reposetup-test/src
1547 reposetup() for $TESTTMP/reposetup-test/src
1544 reposetup() for $TESTTMP/reposetup-test/push-dst3
1548 reposetup() for $TESTTMP/reposetup-test/push-dst3
1545 $ hg init pull-src3
1549 $ hg init pull-src3
1546 reposetup() for $TESTTMP/reposetup-test/pull-src3
1550 reposetup() for $TESTTMP/reposetup-test/pull-src3
1547 $ hg -q -R pull-src3 pull src
1551 $ hg -q -R pull-src3 pull src
1548 reposetup() for $TESTTMP/reposetup-test/pull-src3
1552 reposetup() for $TESTTMP/reposetup-test/pull-src3
1549 reposetup() for $TESTTMP/reposetup-test/src
1553 reposetup() for $TESTTMP/reposetup-test/src
1550
1554
1551 $ echo '[extensions]' >> src/.hg/hgrc
1555 $ echo '[extensions]' >> src/.hg/hgrc
1552 $ echo '# disable extension locally' >> src/.hg/hgrc
1556 $ echo '# disable extension locally' >> src/.hg/hgrc
1553 $ echo 'reposetuptest = !' >> src/.hg/hgrc
1557 $ echo 'reposetuptest = !' >> src/.hg/hgrc
1554 $ hg clone -U src clone-dst4
1558 $ hg clone -U src clone-dst4
1555 reposetup() for $TESTTMP/reposetup-test/clone-dst4
1559 reposetup() for $TESTTMP/reposetup-test/clone-dst4
1556 $ hg init push-dst4
1560 $ hg init push-dst4
1557 reposetup() for $TESTTMP/reposetup-test/push-dst4
1561 reposetup() for $TESTTMP/reposetup-test/push-dst4
1558 $ hg -q -R src push push-dst4
1562 $ hg -q -R src push push-dst4
1559 reposetup() for $TESTTMP/reposetup-test/push-dst4
1563 reposetup() for $TESTTMP/reposetup-test/push-dst4
1560 $ hg init pull-src4
1564 $ hg init pull-src4
1561 reposetup() for $TESTTMP/reposetup-test/pull-src4
1565 reposetup() for $TESTTMP/reposetup-test/pull-src4
1562 $ hg -q -R pull-src4 pull src
1566 $ hg -q -R pull-src4 pull src
1563 reposetup() for $TESTTMP/reposetup-test/pull-src4
1567 reposetup() for $TESTTMP/reposetup-test/pull-src4
1564
1568
1565 disabling in command line overlays with all configuration
1569 disabling in command line overlays with all configuration
1566 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
1570 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
1567 $ hg --config extensions.reposetuptest=! init push-dst5
1571 $ hg --config extensions.reposetuptest=! init push-dst5
1568 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
1572 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
1569 $ hg --config extensions.reposetuptest=! init pull-src5
1573 $ hg --config extensions.reposetuptest=! init pull-src5
1570 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
1574 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
1571
1575
1572 $ cat <<EOF >> $HGRCPATH
1576 $ cat <<EOF >> $HGRCPATH
1573 > [extensions]
1577 > [extensions]
1574 > # disable extension globally and explicitly
1578 > # disable extension globally and explicitly
1575 > reposetuptest = !
1579 > reposetuptest = !
1576 > EOF
1580 > EOF
1577 $ hg init parent
1581 $ hg init parent
1578 $ hg init parent/sub1
1582 $ hg init parent/sub1
1579 $ echo 1 > parent/sub1/1
1583 $ echo 1 > parent/sub1/1
1580 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
1584 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
1581 adding 1
1585 adding 1
1582 $ hg init parent/sub2
1586 $ hg init parent/sub2
1583 $ hg init parent/sub2/sub21
1587 $ hg init parent/sub2/sub21
1584 $ echo 21 > parent/sub2/sub21/21
1588 $ echo 21 > parent/sub2/sub21/21
1585 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
1589 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
1586 adding 21
1590 adding 21
1587 $ cat > parent/sub2/.hgsub <<EOF
1591 $ cat > parent/sub2/.hgsub <<EOF
1588 > sub21 = sub21
1592 > sub21 = sub21
1589 > EOF
1593 > EOF
1590 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
1594 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
1591 adding .hgsub
1595 adding .hgsub
1592 $ hg init parent/sub3
1596 $ hg init parent/sub3
1593 $ echo 3 > parent/sub3/3
1597 $ echo 3 > parent/sub3/3
1594 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
1598 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
1595 adding 3
1599 adding 3
1596 $ cat > parent/.hgsub <<EOF
1600 $ cat > parent/.hgsub <<EOF
1597 > sub1 = sub1
1601 > sub1 = sub1
1598 > sub2 = sub2
1602 > sub2 = sub2
1599 > sub3 = sub3
1603 > sub3 = sub3
1600 > EOF
1604 > EOF
1601 $ hg -R parent commit -Am '#0 at parent'
1605 $ hg -R parent commit -Am '#0 at parent'
1602 adding .hgsub
1606 adding .hgsub
1603 $ echo '[extensions]' >> parent/.hg/hgrc
1607 $ echo '[extensions]' >> parent/.hg/hgrc
1604 $ echo '# enable extension locally' >> parent/.hg/hgrc
1608 $ echo '# enable extension locally' >> parent/.hg/hgrc
1605 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
1609 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
1606 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
1610 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
1607 $ hg -R parent status -S -A
1611 $ hg -R parent status -S -A
1608 reposetup() for $TESTTMP/reposetup-test/parent
1612 reposetup() for $TESTTMP/reposetup-test/parent
1609 reposetup() for $TESTTMP/reposetup-test/parent/sub2
1613 reposetup() for $TESTTMP/reposetup-test/parent/sub2
1610 C .hgsub
1614 C .hgsub
1611 C .hgsubstate
1615 C .hgsubstate
1612 C sub1/1
1616 C sub1/1
1613 C sub2/.hgsub
1617 C sub2/.hgsub
1614 C sub2/.hgsubstate
1618 C sub2/.hgsubstate
1615 C sub2/sub21/21
1619 C sub2/sub21/21
1616 C sub3/3
1620 C sub3/3
1617
1621
1618 $ cd ..
1622 $ cd ..
1619
1623
1620 Prohibit registration of commands that don't use @command (issue5137)
1624 Prohibit registration of commands that don't use @command (issue5137)
1621
1625
1622 $ hg init deprecated
1626 $ hg init deprecated
1623 $ cd deprecated
1627 $ cd deprecated
1624
1628
1625 $ cat <<EOF > deprecatedcmd.py
1629 $ cat <<EOF > deprecatedcmd.py
1626 > def deprecatedcmd(repo, ui):
1630 > def deprecatedcmd(repo, ui):
1627 > pass
1631 > pass
1628 > cmdtable = {
1632 > cmdtable = {
1629 > b'deprecatedcmd': (deprecatedcmd, [], b''),
1633 > b'deprecatedcmd': (deprecatedcmd, [], b''),
1630 > }
1634 > }
1631 > EOF
1635 > EOF
1632 $ cat <<EOF > .hg/hgrc
1636 $ cat <<EOF > .hg/hgrc
1633 > [extensions]
1637 > [extensions]
1634 > deprecatedcmd = `pwd`/deprecatedcmd.py
1638 > deprecatedcmd = `pwd`/deprecatedcmd.py
1635 > mq = !
1639 > mq = !
1636 > hgext.mq = !
1640 > hgext.mq = !
1637 > hgext/mq = !
1641 > hgext/mq = !
1638 > EOF
1642 > EOF
1639
1643
1640 $ hg deprecatedcmd > /dev/null
1644 $ hg deprecatedcmd > /dev/null
1641 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1645 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1642 *** (use @command decorator to register 'deprecatedcmd')
1646 *** (use @command decorator to register 'deprecatedcmd')
1643 hg: unknown command 'deprecatedcmd'
1647 hg: unknown command 'deprecatedcmd'
1644 [255]
1648 [255]
1645
1649
1646 the extension shouldn't be loaded at all so the mq works:
1650 the extension shouldn't be loaded at all so the mq works:
1647
1651
1648 $ hg qseries --config extensions.mq= > /dev/null
1652 $ hg qseries --config extensions.mq= > /dev/null
1649 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1653 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1650 *** (use @command decorator to register 'deprecatedcmd')
1654 *** (use @command decorator to register 'deprecatedcmd')
1651
1655
1652 $ cd ..
1656 $ cd ..
1653
1657
1654 Test synopsis and docstring extending
1658 Test synopsis and docstring extending
1655
1659
1656 $ hg init exthelp
1660 $ hg init exthelp
1657 $ cat > exthelp.py <<EOF
1661 $ cat > exthelp.py <<EOF
1658 > from mercurial import commands, extensions
1662 > from mercurial import commands, extensions
1659 > def exbookmarks(orig, *args, **opts):
1663 > def exbookmarks(orig, *args, **opts):
1660 > return orig(*args, **opts)
1664 > return orig(*args, **opts)
1661 > def uisetup(ui):
1665 > def uisetup(ui):
1662 > synopsis = ' GREPME [--foo] [-x]'
1666 > synopsis = ' GREPME [--foo] [-x]'
1663 > docstring = '''
1667 > docstring = '''
1664 > GREPME make sure that this is in the help!
1668 > GREPME make sure that this is in the help!
1665 > '''
1669 > '''
1666 > extensions.wrapcommand(commands.table, b'bookmarks', exbookmarks,
1670 > extensions.wrapcommand(commands.table, b'bookmarks', exbookmarks,
1667 > synopsis, docstring)
1671 > synopsis, docstring)
1668 > EOF
1672 > EOF
1669 $ abspath=`pwd`/exthelp.py
1673 $ abspath=`pwd`/exthelp.py
1670 $ echo '[extensions]' >> $HGRCPATH
1674 $ echo '[extensions]' >> $HGRCPATH
1671 $ echo "exthelp = $abspath" >> $HGRCPATH
1675 $ echo "exthelp = $abspath" >> $HGRCPATH
1672 $ cd exthelp
1676 $ cd exthelp
1673 $ hg help bookmarks | grep GREPME
1677 $ hg help bookmarks | grep GREPME
1674 hg bookmarks [OPTIONS]... [NAME]... GREPME [--foo] [-x]
1678 hg bookmarks [OPTIONS]... [NAME]... GREPME [--foo] [-x]
1675 GREPME make sure that this is in the help!
1679 GREPME make sure that this is in the help!
1676 $ cd ..
1680 $ cd ..
1677
1681
1678 Show deprecation warning for the use of cmdutil.command
1682 Show deprecation warning for the use of cmdutil.command
1679
1683
1680 $ cat > nonregistrar.py <<EOF
1684 $ cat > nonregistrar.py <<EOF
1681 > from mercurial import cmdutil
1685 > from mercurial import cmdutil
1682 > cmdtable = {}
1686 > cmdtable = {}
1683 > command = cmdutil.command(cmdtable)
1687 > command = cmdutil.command(cmdtable)
1684 > @command(b'foo', [], norepo=True)
1688 > @command(b'foo', [], norepo=True)
1685 > def foo(ui):
1689 > def foo(ui):
1686 > pass
1690 > pass
1687 > EOF
1691 > EOF
1688
1692
1689 $ hg --config extensions.nonregistrar=`pwd`/nonregistrar.py version > /dev/null
1693 $ hg --config extensions.nonregistrar=`pwd`/nonregistrar.py version > /dev/null
1690 devel-warn: cmdutil.command is deprecated, use registrar.command to register 'foo'
1694 devel-warn: cmdutil.command is deprecated, use registrar.command to register 'foo'
1691 (compatibility will be dropped after Mercurial-4.6, update your code.) * (glob)
1695 (compatibility will be dropped after Mercurial-4.6, update your code.) * (glob)
1692
1696
1693 Prohibit the use of unicode strings as the default value of options
1697 Prohibit the use of unicode strings as the default value of options
1694
1698
1695 $ hg init $TESTTMP/opt-unicode-default
1699 $ hg init $TESTTMP/opt-unicode-default
1696
1700
1697 $ cat > $TESTTMP/test_unicode_default_value.py << EOF
1701 $ cat > $TESTTMP/test_unicode_default_value.py << EOF
1698 > from mercurial import registrar
1702 > from mercurial import registrar
1699 > cmdtable = {}
1703 > cmdtable = {}
1700 > command = registrar.command(cmdtable)
1704 > command = registrar.command(cmdtable)
1701 > @command(b'dummy', [('', 'opt', u'value', u'help')], 'ext [OPTIONS]')
1705 > @command(b'dummy', [('', 'opt', u'value', u'help')], 'ext [OPTIONS]')
1702 > def ext(*args, **opts):
1706 > def ext(*args, **opts):
1703 > print(opts['opt'])
1707 > print(opts['opt'])
1704 > EOF
1708 > EOF
1705 $ cat > $TESTTMP/opt-unicode-default/.hg/hgrc << EOF
1709 $ cat > $TESTTMP/opt-unicode-default/.hg/hgrc << EOF
1706 > [extensions]
1710 > [extensions]
1707 > test_unicode_default_value = $TESTTMP/test_unicode_default_value.py
1711 > test_unicode_default_value = $TESTTMP/test_unicode_default_value.py
1708 > EOF
1712 > EOF
1709 $ hg -R $TESTTMP/opt-unicode-default dummy
1713 $ hg -R $TESTTMP/opt-unicode-default dummy
1710 *** failed to import extension test_unicode_default_value from $TESTTMP/test_unicode_default_value.py: unicode u'value' found in cmdtable.dummy
1714 *** failed to import extension test_unicode_default_value from $TESTTMP/test_unicode_default_value.py: unicode u'value' found in cmdtable.dummy
1711 *** (use b'' to make it byte string)
1715 *** (use b'' to make it byte string)
1712 hg: unknown command 'dummy'
1716 hg: unknown command 'dummy'
1713 (did you mean summary?)
1717 (did you mean summary?)
1714 [255]
1718 [255]
General Comments 0
You need to be logged in to leave comments. Login now