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