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