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