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