##// END OF EJS Templates
extdiff: drop the command alias without options example in the help text...
Matt Harbison -
r23151:733d980b default
parent child Browse files
Show More
@@ -1,334 +1,331 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 The extdiff extension also allows you to configure new diff commands, so
16 The extdiff extension also allows you to configure new diff commands, so
17 you do not need to type :hg:`extdiff -p kdiff3` always. ::
17 you do not need to type :hg:`extdiff -p kdiff3` always. ::
18
18
19 [extdiff]
19 [extdiff]
20 # add new command that runs GNU diff(1) in 'context diff' mode
20 # add new command that runs GNU diff(1) in 'context diff' mode
21 cdiff = gdiff -Nprc5
21 cdiff = gdiff -Nprc5
22 ## or the old way:
22 ## or the old way:
23 #cmd.cdiff = gdiff
23 #cmd.cdiff = gdiff
24 #opts.cdiff = -Nprc5
24 #opts.cdiff = -Nprc5
25
25
26 # add new command called vdiff, runs kdiff3
27 vdiff = kdiff3
28
29 # add new command called meld, runs meld (no need to name twice). If
26 # add new command called meld, runs meld (no need to name twice). If
30 # the meld executable is not available, the meld tool in [merge-tools]
27 # the meld executable is not available, the meld tool in [merge-tools]
31 # will be used, if available
28 # will be used, if available
32 meld =
29 meld =
33
30
34 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
31 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
35 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
32 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
36 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
33 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
37 # your .vimrc
34 # your .vimrc
38 vimdiff = gvim -f "+next" \\
35 vimdiff = gvim -f "+next" \\
39 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
36 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
40
37
41 Tool arguments can include variables that are expanded at runtime::
38 Tool arguments can include variables that are expanded at runtime::
42
39
43 $parent1, $plabel1 - filename, descriptive label of first parent
40 $parent1, $plabel1 - filename, descriptive label of first parent
44 $child, $clabel - filename, descriptive label of child revision
41 $child, $clabel - filename, descriptive label of child revision
45 $parent2, $plabel2 - filename, descriptive label of second parent
42 $parent2, $plabel2 - filename, descriptive label of second parent
46 $root - repository root
43 $root - repository root
47 $parent is an alias for $parent1.
44 $parent is an alias for $parent1.
48
45
49 The extdiff extension will look in your [diff-tools] and [merge-tools]
46 The extdiff extension will look in your [diff-tools] and [merge-tools]
50 sections for diff tool arguments, when none are specified in [extdiff].
47 sections for diff tool arguments, when none are specified in [extdiff].
51
48
52 ::
49 ::
53
50
54 [extdiff]
51 [extdiff]
55 kdiff3 =
52 kdiff3 =
56
53
57 [diff-tools]
54 [diff-tools]
58 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
55 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
59
56
60 You can use -I/-X and list of file or directory names like normal
57 You can use -I/-X and list of file or directory names like normal
61 :hg:`diff` command. The extdiff extension makes snapshots of only
58 :hg:`diff` command. The extdiff extension makes snapshots of only
62 needed files, so running the external diff program will actually be
59 needed files, so running the external diff program will actually be
63 pretty fast (at least faster than having to compare the entire tree).
60 pretty fast (at least faster than having to compare the entire tree).
64 '''
61 '''
65
62
66 from mercurial.i18n import _
63 from mercurial.i18n import _
67 from mercurial.node import short, nullid
64 from mercurial.node import short, nullid
68 from mercurial import cmdutil, scmutil, util, commands, encoding, filemerge
65 from mercurial import cmdutil, scmutil, util, commands, encoding, filemerge
69 import os, shlex, shutil, tempfile, re
66 import os, shlex, shutil, tempfile, re
70
67
71 cmdtable = {}
68 cmdtable = {}
72 command = cmdutil.command(cmdtable)
69 command = cmdutil.command(cmdtable)
73 testedwith = 'internal'
70 testedwith = 'internal'
74
71
75 def snapshot(ui, repo, files, node, tmproot):
72 def snapshot(ui, repo, files, node, tmproot):
76 '''snapshot files as of some revision
73 '''snapshot files as of some revision
77 if not using snapshot, -I/-X does not work and recursive diff
74 if not using snapshot, -I/-X does not work and recursive diff
78 in tools like kdiff3 and meld displays too many files.'''
75 in tools like kdiff3 and meld displays too many files.'''
79 dirname = os.path.basename(repo.root)
76 dirname = os.path.basename(repo.root)
80 if dirname == "":
77 if dirname == "":
81 dirname = "root"
78 dirname = "root"
82 if node is not None:
79 if node is not None:
83 dirname = '%s.%s' % (dirname, short(node))
80 dirname = '%s.%s' % (dirname, short(node))
84 base = os.path.join(tmproot, dirname)
81 base = os.path.join(tmproot, dirname)
85 os.mkdir(base)
82 os.mkdir(base)
86 if node is not None:
83 if node is not None:
87 ui.note(_('making snapshot of %d files from rev %s\n') %
84 ui.note(_('making snapshot of %d files from rev %s\n') %
88 (len(files), short(node)))
85 (len(files), short(node)))
89 else:
86 else:
90 ui.note(_('making snapshot of %d files from working directory\n') %
87 ui.note(_('making snapshot of %d files from working directory\n') %
91 (len(files)))
88 (len(files)))
92 wopener = scmutil.opener(base)
89 wopener = scmutil.opener(base)
93 fns_and_mtime = []
90 fns_and_mtime = []
94 ctx = repo[node]
91 ctx = repo[node]
95 for fn in sorted(files):
92 for fn in sorted(files):
96 wfn = util.pconvert(fn)
93 wfn = util.pconvert(fn)
97 if wfn not in ctx:
94 if wfn not in ctx:
98 # File doesn't exist; could be a bogus modify
95 # File doesn't exist; could be a bogus modify
99 continue
96 continue
100 ui.note(' %s\n' % wfn)
97 ui.note(' %s\n' % wfn)
101 dest = os.path.join(base, wfn)
98 dest = os.path.join(base, wfn)
102 fctx = ctx[wfn]
99 fctx = ctx[wfn]
103 data = repo.wwritedata(wfn, fctx.data())
100 data = repo.wwritedata(wfn, fctx.data())
104 if 'l' in fctx.flags():
101 if 'l' in fctx.flags():
105 wopener.symlink(data, wfn)
102 wopener.symlink(data, wfn)
106 else:
103 else:
107 wopener.write(wfn, data)
104 wopener.write(wfn, data)
108 if 'x' in fctx.flags():
105 if 'x' in fctx.flags():
109 util.setflags(dest, False, True)
106 util.setflags(dest, False, True)
110 if node is None:
107 if node is None:
111 fns_and_mtime.append((dest, repo.wjoin(fn),
108 fns_and_mtime.append((dest, repo.wjoin(fn),
112 os.lstat(dest).st_mtime))
109 os.lstat(dest).st_mtime))
113 return dirname, fns_and_mtime
110 return dirname, fns_and_mtime
114
111
115 def dodiff(ui, repo, diffcmd, diffopts, pats, opts):
112 def dodiff(ui, repo, diffcmd, diffopts, pats, opts):
116 '''Do the actual diff:
113 '''Do the actual diff:
117
114
118 - copy to a temp structure if diffing 2 internal revisions
115 - copy to a temp structure if diffing 2 internal revisions
119 - copy to a temp structure if diffing working revision with
116 - copy to a temp structure if diffing working revision with
120 another one and more than 1 file is changed
117 another one and more than 1 file is changed
121 - just invoke the diff for a single file in the working dir
118 - just invoke the diff for a single file in the working dir
122 '''
119 '''
123
120
124 revs = opts.get('rev')
121 revs = opts.get('rev')
125 change = opts.get('change')
122 change = opts.get('change')
126 args = ' '.join(diffopts)
123 args = ' '.join(diffopts)
127 do3way = '$parent2' in args
124 do3way = '$parent2' in args
128
125
129 if revs and change:
126 if revs and change:
130 msg = _('cannot specify --rev and --change at the same time')
127 msg = _('cannot specify --rev and --change at the same time')
131 raise util.Abort(msg)
128 raise util.Abort(msg)
132 elif change:
129 elif change:
133 node2 = scmutil.revsingle(repo, change, None).node()
130 node2 = scmutil.revsingle(repo, change, None).node()
134 node1a, node1b = repo.changelog.parents(node2)
131 node1a, node1b = repo.changelog.parents(node2)
135 else:
132 else:
136 node1a, node2 = scmutil.revpair(repo, revs)
133 node1a, node2 = scmutil.revpair(repo, revs)
137 if not revs:
134 if not revs:
138 node1b = repo.dirstate.p2()
135 node1b = repo.dirstate.p2()
139 else:
136 else:
140 node1b = nullid
137 node1b = nullid
141
138
142 # Disable 3-way merge if there is only one parent
139 # Disable 3-way merge if there is only one parent
143 if do3way:
140 if do3way:
144 if node1b == nullid:
141 if node1b == nullid:
145 do3way = False
142 do3way = False
146
143
147 matcher = scmutil.match(repo[node2], pats, opts)
144 matcher = scmutil.match(repo[node2], pats, opts)
148 mod_a, add_a, rem_a = map(set, repo.status(node1a, node2, matcher)[:3])
145 mod_a, add_a, rem_a = map(set, repo.status(node1a, node2, matcher)[:3])
149 if do3way:
146 if do3way:
150 mod_b, add_b, rem_b = map(set, repo.status(node1b, node2, matcher)[:3])
147 mod_b, add_b, rem_b = map(set, repo.status(node1b, node2, matcher)[:3])
151 else:
148 else:
152 mod_b, add_b, rem_b = set(), set(), set()
149 mod_b, add_b, rem_b = set(), set(), set()
153 modadd = mod_a | add_a | mod_b | add_b
150 modadd = mod_a | add_a | mod_b | add_b
154 common = modadd | rem_a | rem_b
151 common = modadd | rem_a | rem_b
155 if not common:
152 if not common:
156 return 0
153 return 0
157
154
158 tmproot = tempfile.mkdtemp(prefix='extdiff.')
155 tmproot = tempfile.mkdtemp(prefix='extdiff.')
159 try:
156 try:
160 # Always make a copy of node1a (and node1b, if applicable)
157 # Always make a copy of node1a (and node1b, if applicable)
161 dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a)
158 dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a)
162 dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot)[0]
159 dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot)[0]
163 rev1a = '@%d' % repo[node1a].rev()
160 rev1a = '@%d' % repo[node1a].rev()
164 if do3way:
161 if do3way:
165 dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b)
162 dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b)
166 dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot)[0]
163 dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot)[0]
167 rev1b = '@%d' % repo[node1b].rev()
164 rev1b = '@%d' % repo[node1b].rev()
168 else:
165 else:
169 dir1b = None
166 dir1b = None
170 rev1b = ''
167 rev1b = ''
171
168
172 fns_and_mtime = []
169 fns_and_mtime = []
173
170
174 # If node2 in not the wc or there is >1 change, copy it
171 # If node2 in not the wc or there is >1 change, copy it
175 dir2root = ''
172 dir2root = ''
176 rev2 = ''
173 rev2 = ''
177 if node2:
174 if node2:
178 dir2 = snapshot(ui, repo, modadd, node2, tmproot)[0]
175 dir2 = snapshot(ui, repo, modadd, node2, tmproot)[0]
179 rev2 = '@%d' % repo[node2].rev()
176 rev2 = '@%d' % repo[node2].rev()
180 elif len(common) > 1:
177 elif len(common) > 1:
181 #we only actually need to get the files to copy back to
178 #we only actually need to get the files to copy back to
182 #the working dir in this case (because the other cases
179 #the working dir in this case (because the other cases
183 #are: diffing 2 revisions or single file -- in which case
180 #are: diffing 2 revisions or single file -- in which case
184 #the file is already directly passed to the diff tool).
181 #the file is already directly passed to the diff tool).
185 dir2, fns_and_mtime = snapshot(ui, repo, modadd, None, tmproot)
182 dir2, fns_and_mtime = snapshot(ui, repo, modadd, None, tmproot)
186 else:
183 else:
187 # This lets the diff tool open the changed file directly
184 # This lets the diff tool open the changed file directly
188 dir2 = ''
185 dir2 = ''
189 dir2root = repo.root
186 dir2root = repo.root
190
187
191 label1a = rev1a
188 label1a = rev1a
192 label1b = rev1b
189 label1b = rev1b
193 label2 = rev2
190 label2 = rev2
194
191
195 # If only one change, diff the files instead of the directories
192 # If only one change, diff the files instead of the directories
196 # Handle bogus modifies correctly by checking if the files exist
193 # Handle bogus modifies correctly by checking if the files exist
197 if len(common) == 1:
194 if len(common) == 1:
198 common_file = util.localpath(common.pop())
195 common_file = util.localpath(common.pop())
199 dir1a = os.path.join(tmproot, dir1a, common_file)
196 dir1a = os.path.join(tmproot, dir1a, common_file)
200 label1a = common_file + rev1a
197 label1a = common_file + rev1a
201 if not os.path.isfile(dir1a):
198 if not os.path.isfile(dir1a):
202 dir1a = os.devnull
199 dir1a = os.devnull
203 if do3way:
200 if do3way:
204 dir1b = os.path.join(tmproot, dir1b, common_file)
201 dir1b = os.path.join(tmproot, dir1b, common_file)
205 label1b = common_file + rev1b
202 label1b = common_file + rev1b
206 if not os.path.isfile(dir1b):
203 if not os.path.isfile(dir1b):
207 dir1b = os.devnull
204 dir1b = os.devnull
208 dir2 = os.path.join(dir2root, dir2, common_file)
205 dir2 = os.path.join(dir2root, dir2, common_file)
209 label2 = common_file + rev2
206 label2 = common_file + rev2
210
207
211 # Function to quote file/dir names in the argument string.
208 # Function to quote file/dir names in the argument string.
212 # When not operating in 3-way mode, an empty string is
209 # When not operating in 3-way mode, an empty string is
213 # returned for parent2
210 # returned for parent2
214 replace = {'parent': dir1a, 'parent1': dir1a, 'parent2': dir1b,
211 replace = {'parent': dir1a, 'parent1': dir1a, 'parent2': dir1b,
215 'plabel1': label1a, 'plabel2': label1b,
212 'plabel1': label1a, 'plabel2': label1b,
216 'clabel': label2, 'child': dir2,
213 'clabel': label2, 'child': dir2,
217 'root': repo.root}
214 'root': repo.root}
218 def quote(match):
215 def quote(match):
219 key = match.group()[1:]
216 key = match.group()[1:]
220 if not do3way and key == 'parent2':
217 if not do3way and key == 'parent2':
221 return ''
218 return ''
222 return util.shellquote(replace[key])
219 return util.shellquote(replace[key])
223
220
224 # Match parent2 first, so 'parent1?' will match both parent1 and parent
221 # Match parent2 first, so 'parent1?' will match both parent1 and parent
225 regex = '\$(parent2|parent1?|child|plabel1|plabel2|clabel|root)'
222 regex = '\$(parent2|parent1?|child|plabel1|plabel2|clabel|root)'
226 if not do3way and not re.search(regex, args):
223 if not do3way and not re.search(regex, args):
227 args += ' $parent1 $child'
224 args += ' $parent1 $child'
228 args = re.sub(regex, quote, args)
225 args = re.sub(regex, quote, args)
229 cmdline = util.shellquote(diffcmd) + ' ' + args
226 cmdline = util.shellquote(diffcmd) + ' ' + args
230
227
231 ui.debug('running %r in %s\n' % (cmdline, tmproot))
228 ui.debug('running %r in %s\n' % (cmdline, tmproot))
232 util.system(cmdline, cwd=tmproot, out=ui.fout)
229 util.system(cmdline, cwd=tmproot, out=ui.fout)
233
230
234 for copy_fn, working_fn, mtime in fns_and_mtime:
231 for copy_fn, working_fn, mtime in fns_and_mtime:
235 if os.lstat(copy_fn).st_mtime != mtime:
232 if os.lstat(copy_fn).st_mtime != mtime:
236 ui.debug('file changed while diffing. '
233 ui.debug('file changed while diffing. '
237 'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn))
234 'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn))
238 util.copyfile(copy_fn, working_fn)
235 util.copyfile(copy_fn, working_fn)
239
236
240 return 1
237 return 1
241 finally:
238 finally:
242 ui.note(_('cleaning up temp directory\n'))
239 ui.note(_('cleaning up temp directory\n'))
243 shutil.rmtree(tmproot)
240 shutil.rmtree(tmproot)
244
241
245 @command('extdiff',
242 @command('extdiff',
246 [('p', 'program', '',
243 [('p', 'program', '',
247 _('comparison program to run'), _('CMD')),
244 _('comparison program to run'), _('CMD')),
248 ('o', 'option', [],
245 ('o', 'option', [],
249 _('pass option to comparison program'), _('OPT')),
246 _('pass option to comparison program'), _('OPT')),
250 ('r', 'rev', [], _('revision'), _('REV')),
247 ('r', 'rev', [], _('revision'), _('REV')),
251 ('c', 'change', '', _('change made by revision'), _('REV')),
248 ('c', 'change', '', _('change made by revision'), _('REV')),
252 ] + commands.walkopts,
249 ] + commands.walkopts,
253 _('hg extdiff [OPT]... [FILE]...'),
250 _('hg extdiff [OPT]... [FILE]...'),
254 inferrepo=True)
251 inferrepo=True)
255 def extdiff(ui, repo, *pats, **opts):
252 def extdiff(ui, repo, *pats, **opts):
256 '''use external program to diff repository (or selected files)
253 '''use external program to diff repository (or selected files)
257
254
258 Show differences between revisions for the specified files, using
255 Show differences between revisions for the specified files, using
259 an external program. The default program used is diff, with
256 an external program. The default program used is diff, with
260 default options "-Npru".
257 default options "-Npru".
261
258
262 To select a different program, use the -p/--program option. The
259 To select a different program, use the -p/--program option. The
263 program will be passed the names of two directories to compare. To
260 program will be passed the names of two directories to compare. To
264 pass additional options to the program, use -o/--option. These
261 pass additional options to the program, use -o/--option. These
265 will be passed before the names of the directories to compare.
262 will be passed before the names of the directories to compare.
266
263
267 When two revision arguments are given, then changes are shown
264 When two revision arguments are given, then changes are shown
268 between those revisions. If only one revision is specified then
265 between those revisions. If only one revision is specified then
269 that revision is compared to the working directory, and, when no
266 that revision is compared to the working directory, and, when no
270 revisions are specified, the working directory files are compared
267 revisions are specified, the working directory files are compared
271 to its parent.'''
268 to its parent.'''
272 program = opts.get('program')
269 program = opts.get('program')
273 option = opts.get('option')
270 option = opts.get('option')
274 if not program:
271 if not program:
275 program = 'diff'
272 program = 'diff'
276 option = option or ['-Npru']
273 option = option or ['-Npru']
277 return dodiff(ui, repo, program, option, pats, opts)
274 return dodiff(ui, repo, program, option, pats, opts)
278
275
279 def uisetup(ui):
276 def uisetup(ui):
280 for cmd, path in ui.configitems('extdiff'):
277 for cmd, path in ui.configitems('extdiff'):
281 if cmd.startswith('cmd.'):
278 if cmd.startswith('cmd.'):
282 cmd = cmd[4:]
279 cmd = cmd[4:]
283 if not path:
280 if not path:
284 path = util.findexe(cmd)
281 path = util.findexe(cmd)
285 if path is None:
282 if path is None:
286 path = filemerge.findexternaltool(ui, cmd) or cmd
283 path = filemerge.findexternaltool(ui, cmd) or cmd
287 diffopts = ui.config('extdiff', 'opts.' + cmd, '')
284 diffopts = ui.config('extdiff', 'opts.' + cmd, '')
288 diffopts = diffopts and [diffopts] or []
285 diffopts = diffopts and [diffopts] or []
289 elif cmd.startswith('opts.'):
286 elif cmd.startswith('opts.'):
290 continue
287 continue
291 else:
288 else:
292 # command = path opts
289 # command = path opts
293 if path:
290 if path:
294 diffopts = shlex.split(path)
291 diffopts = shlex.split(path)
295 path = diffopts.pop(0)
292 path = diffopts.pop(0)
296 else:
293 else:
297 path, diffopts = util.findexe(cmd), []
294 path, diffopts = util.findexe(cmd), []
298 if path is None:
295 if path is None:
299 path = filemerge.findexternaltool(ui, cmd) or cmd
296 path = filemerge.findexternaltool(ui, cmd) or cmd
300 # look for diff arguments in [diff-tools] then [merge-tools]
297 # look for diff arguments in [diff-tools] then [merge-tools]
301 if diffopts == []:
298 if diffopts == []:
302 args = ui.config('diff-tools', cmd+'.diffargs') or \
299 args = ui.config('diff-tools', cmd+'.diffargs') or \
303 ui.config('merge-tools', cmd+'.diffargs')
300 ui.config('merge-tools', cmd+'.diffargs')
304 if args:
301 if args:
305 diffopts = shlex.split(args)
302 diffopts = shlex.split(args)
306 def save(cmd, path, diffopts):
303 def save(cmd, path, diffopts):
307 '''use closure to save diff command to use'''
304 '''use closure to save diff command to use'''
308 def mydiff(ui, repo, *pats, **opts):
305 def mydiff(ui, repo, *pats, **opts):
309 return dodiff(ui, repo, path, diffopts + opts['option'],
306 return dodiff(ui, repo, path, diffopts + opts['option'],
310 pats, opts)
307 pats, opts)
311 doc = _('''\
308 doc = _('''\
312 use %(path)s to diff repository (or selected files)
309 use %(path)s to diff repository (or selected files)
313
310
314 Show differences between revisions for the specified files, using
311 Show differences between revisions for the specified files, using
315 the %(path)s program.
312 the %(path)s program.
316
313
317 When two revision arguments are given, then changes are shown
314 When two revision arguments are given, then changes are shown
318 between those revisions. If only one revision is specified then
315 between those revisions. If only one revision is specified then
319 that revision is compared to the working directory, and, when no
316 that revision is compared to the working directory, and, when no
320 revisions are specified, the working directory files are compared
317 revisions are specified, the working directory files are compared
321 to its parent.\
318 to its parent.\
322 ''') % {'path': util.uirepr(path)}
319 ''') % {'path': util.uirepr(path)}
323
320
324 # We must translate the docstring right away since it is
321 # We must translate the docstring right away since it is
325 # used as a format string. The string will unfortunately
322 # used as a format string. The string will unfortunately
326 # be translated again in commands.helpcmd and this will
323 # be translated again in commands.helpcmd and this will
327 # fail when the docstring contains non-ASCII characters.
324 # fail when the docstring contains non-ASCII characters.
328 # Decoding the string to a Unicode string here (using the
325 # Decoding the string to a Unicode string here (using the
329 # right encoding) prevents that.
326 # right encoding) prevents that.
330 mydiff.__doc__ = doc.decode(encoding.encoding)
327 mydiff.__doc__ = doc.decode(encoding.encoding)
331 return mydiff
328 return mydiff
332 cmdtable[cmd] = (save(cmd, path, diffopts),
329 cmdtable[cmd] = (save(cmd, path, diffopts),
333 cmdtable['extdiff'][1][1:],
330 cmdtable['extdiff'][1][1:],
334 _('hg %s [OPTION]... [FILE]...') % cmd)
331 _('hg %s [OPTION]... [FILE]...') % cmd)
@@ -1,912 +1,909 b''
1 Test basic extension support
1 Test basic extension support
2
2
3 $ cat > foobar.py <<EOF
3 $ cat > foobar.py <<EOF
4 > import os
4 > import os
5 > from mercurial import cmdutil, commands
5 > from mercurial import cmdutil, commands
6 > cmdtable = {}
6 > cmdtable = {}
7 > command = cmdutil.command(cmdtable)
7 > command = cmdutil.command(cmdtable)
8 > def uisetup(ui):
8 > def uisetup(ui):
9 > ui.write("uisetup called\\n")
9 > ui.write("uisetup called\\n")
10 > def reposetup(ui, repo):
10 > def reposetup(ui, repo):
11 > ui.write("reposetup called for %s\\n" % os.path.basename(repo.root))
11 > ui.write("reposetup called for %s\\n" % os.path.basename(repo.root))
12 > ui.write("ui %s= repo.ui\\n" % (ui == repo.ui and "=" or "!"))
12 > ui.write("ui %s= repo.ui\\n" % (ui == repo.ui and "=" or "!"))
13 > @command('foo', [], 'hg foo')
13 > @command('foo', [], 'hg foo')
14 > def foo(ui, *args, **kwargs):
14 > def foo(ui, *args, **kwargs):
15 > ui.write("Foo\\n")
15 > ui.write("Foo\\n")
16 > @command('bar', [], 'hg bar', norepo=True)
16 > @command('bar', [], 'hg bar', norepo=True)
17 > def bar(ui, *args, **kwargs):
17 > def bar(ui, *args, **kwargs):
18 > ui.write("Bar\\n")
18 > ui.write("Bar\\n")
19 > EOF
19 > EOF
20 $ abspath=`pwd`/foobar.py
20 $ abspath=`pwd`/foobar.py
21
21
22 $ mkdir barfoo
22 $ mkdir barfoo
23 $ cp foobar.py barfoo/__init__.py
23 $ cp foobar.py barfoo/__init__.py
24 $ barfoopath=`pwd`/barfoo
24 $ barfoopath=`pwd`/barfoo
25
25
26 $ hg init a
26 $ hg init a
27 $ cd a
27 $ cd a
28 $ echo foo > file
28 $ echo foo > file
29 $ hg add file
29 $ hg add file
30 $ hg commit -m 'add file'
30 $ hg commit -m 'add file'
31
31
32 $ echo '[extensions]' >> $HGRCPATH
32 $ echo '[extensions]' >> $HGRCPATH
33 $ echo "foobar = $abspath" >> $HGRCPATH
33 $ echo "foobar = $abspath" >> $HGRCPATH
34 $ hg foo
34 $ hg foo
35 uisetup called
35 uisetup called
36 reposetup called for a
36 reposetup called for a
37 ui == repo.ui
37 ui == repo.ui
38 Foo
38 Foo
39
39
40 $ cd ..
40 $ cd ..
41 $ hg clone a b
41 $ hg clone a b
42 uisetup called
42 uisetup called
43 reposetup called for a
43 reposetup called for a
44 ui == repo.ui
44 ui == repo.ui
45 reposetup called for b
45 reposetup called for b
46 ui == repo.ui
46 ui == repo.ui
47 updating to branch default
47 updating to branch default
48 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
48 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
49
49
50 $ hg bar
50 $ hg bar
51 uisetup called
51 uisetup called
52 Bar
52 Bar
53 $ echo 'foobar = !' >> $HGRCPATH
53 $ echo 'foobar = !' >> $HGRCPATH
54
54
55 module/__init__.py-style
55 module/__init__.py-style
56
56
57 $ echo "barfoo = $barfoopath" >> $HGRCPATH
57 $ echo "barfoo = $barfoopath" >> $HGRCPATH
58 $ cd a
58 $ cd a
59 $ hg foo
59 $ hg foo
60 uisetup called
60 uisetup called
61 reposetup called for a
61 reposetup called for a
62 ui == repo.ui
62 ui == repo.ui
63 Foo
63 Foo
64 $ echo 'barfoo = !' >> $HGRCPATH
64 $ echo 'barfoo = !' >> $HGRCPATH
65
65
66 Check that extensions are loaded in phases:
66 Check that extensions are loaded in phases:
67
67
68 $ cat > foo.py <<EOF
68 $ cat > foo.py <<EOF
69 > import os
69 > import os
70 > name = os.path.basename(__file__).rsplit('.', 1)[0]
70 > name = os.path.basename(__file__).rsplit('.', 1)[0]
71 > print "1) %s imported" % name
71 > print "1) %s imported" % name
72 > def uisetup(ui):
72 > def uisetup(ui):
73 > print "2) %s uisetup" % name
73 > print "2) %s uisetup" % name
74 > def extsetup():
74 > def extsetup():
75 > print "3) %s extsetup" % name
75 > print "3) %s extsetup" % name
76 > def reposetup(ui, repo):
76 > def reposetup(ui, repo):
77 > print "4) %s reposetup" % name
77 > print "4) %s reposetup" % name
78 > EOF
78 > EOF
79
79
80 $ cp foo.py bar.py
80 $ cp foo.py bar.py
81 $ echo 'foo = foo.py' >> $HGRCPATH
81 $ echo 'foo = foo.py' >> $HGRCPATH
82 $ echo 'bar = bar.py' >> $HGRCPATH
82 $ echo 'bar = bar.py' >> $HGRCPATH
83
83
84 Command with no output, we just want to see the extensions loaded:
84 Command with no output, we just want to see the extensions loaded:
85
85
86 $ hg paths
86 $ hg paths
87 1) foo imported
87 1) foo imported
88 1) bar imported
88 1) bar imported
89 2) foo uisetup
89 2) foo uisetup
90 2) bar uisetup
90 2) bar uisetup
91 3) foo extsetup
91 3) foo extsetup
92 3) bar extsetup
92 3) bar extsetup
93 4) foo reposetup
93 4) foo reposetup
94 4) bar reposetup
94 4) bar reposetup
95
95
96 Check hgweb's load order:
96 Check hgweb's load order:
97
97
98 $ cat > hgweb.cgi <<EOF
98 $ cat > hgweb.cgi <<EOF
99 > #!/usr/bin/env python
99 > #!/usr/bin/env python
100 > from mercurial import demandimport; demandimport.enable()
100 > from mercurial import demandimport; demandimport.enable()
101 > from mercurial.hgweb import hgweb
101 > from mercurial.hgweb import hgweb
102 > from mercurial.hgweb import wsgicgi
102 > from mercurial.hgweb import wsgicgi
103 > application = hgweb('.', 'test repo')
103 > application = hgweb('.', 'test repo')
104 > wsgicgi.launch(application)
104 > wsgicgi.launch(application)
105 > EOF
105 > EOF
106
106
107 $ REQUEST_METHOD='GET' PATH_INFO='/' SCRIPT_NAME='' QUERY_STRING='' \
107 $ REQUEST_METHOD='GET' PATH_INFO='/' SCRIPT_NAME='' QUERY_STRING='' \
108 > SERVER_PORT='80' SERVER_NAME='localhost' python hgweb.cgi \
108 > SERVER_PORT='80' SERVER_NAME='localhost' python hgweb.cgi \
109 > | grep '^[0-9]) ' # ignores HTML output
109 > | grep '^[0-9]) ' # ignores HTML output
110 1) foo imported
110 1) foo imported
111 1) bar imported
111 1) bar imported
112 2) foo uisetup
112 2) foo uisetup
113 2) bar uisetup
113 2) bar uisetup
114 3) foo extsetup
114 3) foo extsetup
115 3) bar extsetup
115 3) bar extsetup
116 4) foo reposetup
116 4) foo reposetup
117 4) bar reposetup
117 4) bar reposetup
118 4) foo reposetup
118 4) foo reposetup
119 4) bar reposetup
119 4) bar reposetup
120
120
121 $ echo 'foo = !' >> $HGRCPATH
121 $ echo 'foo = !' >> $HGRCPATH
122 $ echo 'bar = !' >> $HGRCPATH
122 $ echo 'bar = !' >> $HGRCPATH
123
123
124 Check "from __future__ import absolute_import" support for external libraries
124 Check "from __future__ import absolute_import" support for external libraries
125
125
126 #if windows
126 #if windows
127 $ PATHSEP=";"
127 $ PATHSEP=";"
128 #else
128 #else
129 $ PATHSEP=":"
129 $ PATHSEP=":"
130 #endif
130 #endif
131 $ export PATHSEP
131 $ export PATHSEP
132
132
133 $ mkdir $TESTTMP/libroot
133 $ mkdir $TESTTMP/libroot
134 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
134 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
135 $ mkdir $TESTTMP/libroot/mod
135 $ mkdir $TESTTMP/libroot/mod
136 $ touch $TESTTMP/libroot/mod/__init__.py
136 $ touch $TESTTMP/libroot/mod/__init__.py
137 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
137 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
138
138
139 #if absimport
139 #if absimport
140 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<EOF
140 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<EOF
141 > from __future__ import absolute_import
141 > from __future__ import absolute_import
142 > import ambig # should load "libroot/ambig.py"
142 > import ambig # should load "libroot/ambig.py"
143 > s = ambig.s
143 > s = ambig.s
144 > EOF
144 > EOF
145 $ cat > loadabs.py <<EOF
145 $ cat > loadabs.py <<EOF
146 > import mod.ambigabs as ambigabs
146 > import mod.ambigabs as ambigabs
147 > def extsetup():
147 > def extsetup():
148 > print 'ambigabs.s=%s' % ambigabs.s
148 > print 'ambigabs.s=%s' % ambigabs.s
149 > EOF
149 > EOF
150 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
150 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
151 ambigabs.s=libroot/ambig.py
151 ambigabs.s=libroot/ambig.py
152 $TESTTMP/a (glob)
152 $TESTTMP/a (glob)
153 #endif
153 #endif
154
154
155 #if no-py3k
155 #if no-py3k
156 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<EOF
156 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<EOF
157 > import ambig # should load "libroot/mod/ambig.py"
157 > import ambig # should load "libroot/mod/ambig.py"
158 > s = ambig.s
158 > s = ambig.s
159 > EOF
159 > EOF
160 $ cat > loadrel.py <<EOF
160 $ cat > loadrel.py <<EOF
161 > import mod.ambigrel as ambigrel
161 > import mod.ambigrel as ambigrel
162 > def extsetup():
162 > def extsetup():
163 > print 'ambigrel.s=%s' % ambigrel.s
163 > print 'ambigrel.s=%s' % ambigrel.s
164 > EOF
164 > EOF
165 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
165 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
166 ambigrel.s=libroot/mod/ambig.py
166 ambigrel.s=libroot/mod/ambig.py
167 $TESTTMP/a (glob)
167 $TESTTMP/a (glob)
168 #endif
168 #endif
169
169
170 Check absolute/relative import of extension specific modules
170 Check absolute/relative import of extension specific modules
171
171
172 $ mkdir $TESTTMP/extroot
172 $ mkdir $TESTTMP/extroot
173 $ cat > $TESTTMP/extroot/bar.py <<EOF
173 $ cat > $TESTTMP/extroot/bar.py <<EOF
174 > s = 'this is extroot.bar'
174 > s = 'this is extroot.bar'
175 > EOF
175 > EOF
176 $ mkdir $TESTTMP/extroot/sub1
176 $ mkdir $TESTTMP/extroot/sub1
177 $ cat > $TESTTMP/extroot/sub1/__init__.py <<EOF
177 $ cat > $TESTTMP/extroot/sub1/__init__.py <<EOF
178 > s = 'this is extroot.sub1.__init__'
178 > s = 'this is extroot.sub1.__init__'
179 > EOF
179 > EOF
180 $ cat > $TESTTMP/extroot/sub1/baz.py <<EOF
180 $ cat > $TESTTMP/extroot/sub1/baz.py <<EOF
181 > s = 'this is extroot.sub1.baz'
181 > s = 'this is extroot.sub1.baz'
182 > EOF
182 > EOF
183 $ cat > $TESTTMP/extroot/__init__.py <<EOF
183 $ cat > $TESTTMP/extroot/__init__.py <<EOF
184 > s = 'this is extroot.__init__'
184 > s = 'this is extroot.__init__'
185 > import foo
185 > import foo
186 > def extsetup(ui):
186 > def extsetup(ui):
187 > ui.write('(extroot) ', foo.func(), '\n')
187 > ui.write('(extroot) ', foo.func(), '\n')
188 > EOF
188 > EOF
189
189
190 $ cat > $TESTTMP/extroot/foo.py <<EOF
190 $ cat > $TESTTMP/extroot/foo.py <<EOF
191 > # test absolute import
191 > # test absolute import
192 > buf = []
192 > buf = []
193 > def func():
193 > def func():
194 > # "not locals" case
194 > # "not locals" case
195 > import extroot.bar
195 > import extroot.bar
196 > buf.append('import extroot.bar in func(): %s' % extroot.bar.s)
196 > buf.append('import extroot.bar in func(): %s' % extroot.bar.s)
197 > return '\n(extroot) '.join(buf)
197 > return '\n(extroot) '.join(buf)
198 > # "fromlist == ('*',)" case
198 > # "fromlist == ('*',)" case
199 > from extroot.bar import *
199 > from extroot.bar import *
200 > buf.append('from extroot.bar import *: %s' % s)
200 > buf.append('from extroot.bar import *: %s' % s)
201 > # "not fromlist" and "if '.' in name" case
201 > # "not fromlist" and "if '.' in name" case
202 > import extroot.sub1.baz
202 > import extroot.sub1.baz
203 > buf.append('import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
203 > buf.append('import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
204 > # "not fromlist" and NOT "if '.' in name" case
204 > # "not fromlist" and NOT "if '.' in name" case
205 > import extroot
205 > import extroot
206 > buf.append('import extroot: %s' % extroot.s)
206 > buf.append('import extroot: %s' % extroot.s)
207 > # NOT "not fromlist" and NOT "level != -1" case
207 > # NOT "not fromlist" and NOT "level != -1" case
208 > from extroot.bar import s
208 > from extroot.bar import s
209 > buf.append('from extroot.bar import s: %s' % s)
209 > buf.append('from extroot.bar import s: %s' % s)
210 > EOF
210 > EOF
211 $ hg --config extensions.extroot=$TESTTMP/extroot root
211 $ hg --config extensions.extroot=$TESTTMP/extroot root
212 (extroot) from extroot.bar import *: this is extroot.bar
212 (extroot) from extroot.bar import *: this is extroot.bar
213 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
213 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
214 (extroot) import extroot: this is extroot.__init__
214 (extroot) import extroot: this is extroot.__init__
215 (extroot) from extroot.bar import s: this is extroot.bar
215 (extroot) from extroot.bar import s: this is extroot.bar
216 (extroot) import extroot.bar in func(): this is extroot.bar
216 (extroot) import extroot.bar in func(): this is extroot.bar
217 $TESTTMP/a (glob)
217 $TESTTMP/a (glob)
218
218
219 #if no-py3k
219 #if no-py3k
220 $ rm "$TESTTMP"/extroot/foo.*
220 $ rm "$TESTTMP"/extroot/foo.*
221 $ cat > $TESTTMP/extroot/foo.py <<EOF
221 $ cat > $TESTTMP/extroot/foo.py <<EOF
222 > # test relative import
222 > # test relative import
223 > buf = []
223 > buf = []
224 > def func():
224 > def func():
225 > # "not locals" case
225 > # "not locals" case
226 > import bar
226 > import bar
227 > buf.append('import bar in func(): %s' % bar.s)
227 > buf.append('import bar in func(): %s' % bar.s)
228 > return '\n(extroot) '.join(buf)
228 > return '\n(extroot) '.join(buf)
229 > # "fromlist == ('*',)" case
229 > # "fromlist == ('*',)" case
230 > from bar import *
230 > from bar import *
231 > buf.append('from bar import *: %s' % s)
231 > buf.append('from bar import *: %s' % s)
232 > # "not fromlist" and "if '.' in name" case
232 > # "not fromlist" and "if '.' in name" case
233 > import sub1.baz
233 > import sub1.baz
234 > buf.append('import sub1.baz: %s' % sub1.baz.s)
234 > buf.append('import sub1.baz: %s' % sub1.baz.s)
235 > # "not fromlist" and NOT "if '.' in name" case
235 > # "not fromlist" and NOT "if '.' in name" case
236 > import sub1
236 > import sub1
237 > buf.append('import sub1: %s' % sub1.s)
237 > buf.append('import sub1: %s' % sub1.s)
238 > # NOT "not fromlist" and NOT "level != -1" case
238 > # NOT "not fromlist" and NOT "level != -1" case
239 > from bar import s
239 > from bar import s
240 > buf.append('from bar import s: %s' % s)
240 > buf.append('from bar import s: %s' % s)
241 > EOF
241 > EOF
242 $ hg --config extensions.extroot=$TESTTMP/extroot root
242 $ hg --config extensions.extroot=$TESTTMP/extroot root
243 (extroot) from bar import *: this is extroot.bar
243 (extroot) from bar import *: this is extroot.bar
244 (extroot) import sub1.baz: this is extroot.sub1.baz
244 (extroot) import sub1.baz: this is extroot.sub1.baz
245 (extroot) import sub1: this is extroot.sub1.__init__
245 (extroot) import sub1: this is extroot.sub1.__init__
246 (extroot) from bar import s: this is extroot.bar
246 (extroot) from bar import s: this is extroot.bar
247 (extroot) import bar in func(): this is extroot.bar
247 (extroot) import bar in func(): this is extroot.bar
248 $TESTTMP/a (glob)
248 $TESTTMP/a (glob)
249 #endif
249 #endif
250
250
251 $ cd ..
251 $ cd ..
252
252
253 hide outer repo
253 hide outer repo
254 $ hg init
254 $ hg init
255
255
256 $ cat > empty.py <<EOF
256 $ cat > empty.py <<EOF
257 > '''empty cmdtable
257 > '''empty cmdtable
258 > '''
258 > '''
259 > cmdtable = {}
259 > cmdtable = {}
260 > EOF
260 > EOF
261 $ emptypath=`pwd`/empty.py
261 $ emptypath=`pwd`/empty.py
262 $ echo "empty = $emptypath" >> $HGRCPATH
262 $ echo "empty = $emptypath" >> $HGRCPATH
263 $ hg help empty
263 $ hg help empty
264 empty extension - empty cmdtable
264 empty extension - empty cmdtable
265
265
266 no commands defined
266 no commands defined
267
267
268
268
269 $ echo 'empty = !' >> $HGRCPATH
269 $ echo 'empty = !' >> $HGRCPATH
270
270
271 $ cat > debugextension.py <<EOF
271 $ cat > debugextension.py <<EOF
272 > '''only debugcommands
272 > '''only debugcommands
273 > '''
273 > '''
274 > from mercurial import cmdutil
274 > from mercurial import cmdutil
275 > cmdtable = {}
275 > cmdtable = {}
276 > command = cmdutil.command(cmdtable)
276 > command = cmdutil.command(cmdtable)
277 > @command('debugfoobar', [], 'hg debugfoobar')
277 > @command('debugfoobar', [], 'hg debugfoobar')
278 > def debugfoobar(ui, repo, *args, **opts):
278 > def debugfoobar(ui, repo, *args, **opts):
279 > "yet another debug command"
279 > "yet another debug command"
280 > pass
280 > pass
281 > @command('foo', [], 'hg foo')
281 > @command('foo', [], 'hg foo')
282 > def foo(ui, repo, *args, **opts):
282 > def foo(ui, repo, *args, **opts):
283 > """yet another foo command
283 > """yet another foo command
284 > This command has been DEPRECATED since forever.
284 > This command has been DEPRECATED since forever.
285 > """
285 > """
286 > pass
286 > pass
287 > EOF
287 > EOF
288 $ debugpath=`pwd`/debugextension.py
288 $ debugpath=`pwd`/debugextension.py
289 $ echo "debugextension = $debugpath" >> $HGRCPATH
289 $ echo "debugextension = $debugpath" >> $HGRCPATH
290
290
291 $ hg help debugextension
291 $ hg help debugextension
292 debugextension extension - only debugcommands
292 debugextension extension - only debugcommands
293
293
294 no commands defined
294 no commands defined
295
295
296
296
297 $ hg --verbose help debugextension
297 $ hg --verbose help debugextension
298 debugextension extension - only debugcommands
298 debugextension extension - only debugcommands
299
299
300 list of commands:
300 list of commands:
301
301
302 foo yet another foo command
302 foo yet another foo command
303
303
304 global options ([+] can be repeated):
304 global options ([+] can be repeated):
305
305
306 -R --repository REPO repository root directory or name of overlay bundle
306 -R --repository REPO repository root directory or name of overlay bundle
307 file
307 file
308 --cwd DIR change working directory
308 --cwd DIR change working directory
309 -y --noninteractive do not prompt, automatically pick the first choice for
309 -y --noninteractive do not prompt, automatically pick the first choice for
310 all prompts
310 all prompts
311 -q --quiet suppress output
311 -q --quiet suppress output
312 -v --verbose enable additional output
312 -v --verbose enable additional output
313 --config CONFIG [+] set/override config option (use 'section.name=value')
313 --config CONFIG [+] set/override config option (use 'section.name=value')
314 --debug enable debugging output
314 --debug enable debugging output
315 --debugger start debugger
315 --debugger start debugger
316 --encoding ENCODE set the charset encoding (default: ascii)
316 --encoding ENCODE set the charset encoding (default: ascii)
317 --encodingmode MODE set the charset encoding mode (default: strict)
317 --encodingmode MODE set the charset encoding mode (default: strict)
318 --traceback always print a traceback on exception
318 --traceback always print a traceback on exception
319 --time time how long the command takes
319 --time time how long the command takes
320 --profile print command execution profile
320 --profile print command execution profile
321 --version output version information and exit
321 --version output version information and exit
322 -h --help display help and exit
322 -h --help display help and exit
323 --hidden consider hidden changesets
323 --hidden consider hidden changesets
324
324
325
325
326
326
327
327
328
328
329
329
330 $ hg --debug help debugextension
330 $ hg --debug help debugextension
331 debugextension extension - only debugcommands
331 debugextension extension - only debugcommands
332
332
333 list of commands:
333 list of commands:
334
334
335 debugfoobar yet another debug command
335 debugfoobar yet another debug command
336 foo yet another foo command
336 foo yet another foo command
337
337
338 global options ([+] can be repeated):
338 global options ([+] can be repeated):
339
339
340 -R --repository REPO repository root directory or name of overlay bundle
340 -R --repository REPO repository root directory or name of overlay bundle
341 file
341 file
342 --cwd DIR change working directory
342 --cwd DIR change working directory
343 -y --noninteractive do not prompt, automatically pick the first choice for
343 -y --noninteractive do not prompt, automatically pick the first choice for
344 all prompts
344 all prompts
345 -q --quiet suppress output
345 -q --quiet suppress output
346 -v --verbose enable additional output
346 -v --verbose enable additional output
347 --config CONFIG [+] set/override config option (use 'section.name=value')
347 --config CONFIG [+] set/override config option (use 'section.name=value')
348 --debug enable debugging output
348 --debug enable debugging output
349 --debugger start debugger
349 --debugger start debugger
350 --encoding ENCODE set the charset encoding (default: ascii)
350 --encoding ENCODE set the charset encoding (default: ascii)
351 --encodingmode MODE set the charset encoding mode (default: strict)
351 --encodingmode MODE set the charset encoding mode (default: strict)
352 --traceback always print a traceback on exception
352 --traceback always print a traceback on exception
353 --time time how long the command takes
353 --time time how long the command takes
354 --profile print command execution profile
354 --profile print command execution profile
355 --version output version information and exit
355 --version output version information and exit
356 -h --help display help and exit
356 -h --help display help and exit
357 --hidden consider hidden changesets
357 --hidden consider hidden changesets
358
358
359
359
360
360
361
361
362
362
363 $ echo 'debugextension = !' >> $HGRCPATH
363 $ echo 'debugextension = !' >> $HGRCPATH
364
364
365 Extension module help vs command help:
365 Extension module help vs command help:
366
366
367 $ echo 'extdiff =' >> $HGRCPATH
367 $ echo 'extdiff =' >> $HGRCPATH
368 $ hg help extdiff
368 $ hg help extdiff
369 hg extdiff [OPT]... [FILE]...
369 hg extdiff [OPT]... [FILE]...
370
370
371 use external program to diff repository (or selected files)
371 use external program to diff repository (or selected files)
372
372
373 Show differences between revisions for the specified files, using an
373 Show differences between revisions for the specified files, using an
374 external program. The default program used is diff, with default options
374 external program. The default program used is diff, with default options
375 "-Npru".
375 "-Npru".
376
376
377 To select a different program, use the -p/--program option. The program
377 To select a different program, use the -p/--program option. The program
378 will be passed the names of two directories to compare. To pass additional
378 will be passed the names of two directories to compare. To pass additional
379 options to the program, use -o/--option. These will be passed before the
379 options to the program, use -o/--option. These will be passed before the
380 names of the directories to compare.
380 names of the directories to compare.
381
381
382 When two revision arguments are given, then changes are shown between
382 When two revision arguments are given, then changes are shown between
383 those revisions. If only one revision is specified then that revision is
383 those revisions. If only one revision is specified then that revision is
384 compared to the working directory, and, when no revisions are specified,
384 compared to the working directory, and, when no revisions are specified,
385 the working directory files are compared to its parent.
385 the working directory files are compared to its parent.
386
386
387 (use "hg help -e extdiff" to show help for the extdiff extension)
387 (use "hg help -e extdiff" to show help for the extdiff extension)
388
388
389 options ([+] can be repeated):
389 options ([+] can be repeated):
390
390
391 -p --program CMD comparison program to run
391 -p --program CMD comparison program to run
392 -o --option OPT [+] pass option to comparison program
392 -o --option OPT [+] pass option to comparison program
393 -r --rev REV [+] revision
393 -r --rev REV [+] revision
394 -c --change REV change made by revision
394 -c --change REV change made by revision
395 -I --include PATTERN [+] include names matching the given patterns
395 -I --include PATTERN [+] include names matching the given patterns
396 -X --exclude PATTERN [+] exclude names matching the given patterns
396 -X --exclude PATTERN [+] exclude names matching the given patterns
397
397
398 (some details hidden, use --verbose to show complete help)
398 (some details hidden, use --verbose to show complete help)
399
399
400
400
401
401
402
402
403
403
404
404
405
405
406
406
407
407
408
408
409 $ hg help --extension extdiff
409 $ hg help --extension extdiff
410 extdiff extension - command to allow external programs to compare revisions
410 extdiff extension - command to allow external programs to compare revisions
411
411
412 The extdiff Mercurial extension allows you to use external programs to compare
412 The extdiff Mercurial extension allows you to use external programs to compare
413 revisions, or revision with working directory. The external diff programs are
413 revisions, or revision with working directory. The external diff programs are
414 called with a configurable set of options and two non-option arguments: paths
414 called with a configurable set of options and two non-option arguments: paths
415 to directories containing snapshots of files to compare.
415 to directories containing snapshots of files to compare.
416
416
417 The extdiff extension also allows you to configure new diff commands, so you
417 The extdiff extension also allows you to configure new diff commands, so you
418 do not need to type "hg extdiff -p kdiff3" always.
418 do not need to type "hg extdiff -p kdiff3" always.
419
419
420 [extdiff]
420 [extdiff]
421 # add new command that runs GNU diff(1) in 'context diff' mode
421 # add new command that runs GNU diff(1) in 'context diff' mode
422 cdiff = gdiff -Nprc5
422 cdiff = gdiff -Nprc5
423 ## or the old way:
423 ## or the old way:
424 #cmd.cdiff = gdiff
424 #cmd.cdiff = gdiff
425 #opts.cdiff = -Nprc5
425 #opts.cdiff = -Nprc5
426
426
427 # add new command called vdiff, runs kdiff3
428 vdiff = kdiff3
429
430 # add new command called meld, runs meld (no need to name twice). If
427 # add new command called meld, runs meld (no need to name twice). If
431 # the meld executable is not available, the meld tool in [merge-tools]
428 # the meld executable is not available, the meld tool in [merge-tools]
432 # will be used, if available
429 # will be used, if available
433 meld =
430 meld =
434
431
435 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
432 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
436 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
433 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
437 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
434 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
438 # your .vimrc
435 # your .vimrc
439 vimdiff = gvim -f "+next" \
436 vimdiff = gvim -f "+next" \
440 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
437 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
441
438
442 Tool arguments can include variables that are expanded at runtime:
439 Tool arguments can include variables that are expanded at runtime:
443
440
444 $parent1, $plabel1 - filename, descriptive label of first parent
441 $parent1, $plabel1 - filename, descriptive label of first parent
445 $child, $clabel - filename, descriptive label of child revision
442 $child, $clabel - filename, descriptive label of child revision
446 $parent2, $plabel2 - filename, descriptive label of second parent
443 $parent2, $plabel2 - filename, descriptive label of second parent
447 $root - repository root
444 $root - repository root
448 $parent is an alias for $parent1.
445 $parent is an alias for $parent1.
449
446
450 The extdiff extension will look in your [diff-tools] and [merge-tools]
447 The extdiff extension will look in your [diff-tools] and [merge-tools]
451 sections for diff tool arguments, when none are specified in [extdiff].
448 sections for diff tool arguments, when none are specified in [extdiff].
452
449
453 [extdiff]
450 [extdiff]
454 kdiff3 =
451 kdiff3 =
455
452
456 [diff-tools]
453 [diff-tools]
457 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
454 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
458
455
459 You can use -I/-X and list of file or directory names like normal "hg diff"
456 You can use -I/-X and list of file or directory names like normal "hg diff"
460 command. The extdiff extension makes snapshots of only needed files, so
457 command. The extdiff extension makes snapshots of only needed files, so
461 running the external diff program will actually be pretty fast (at least
458 running the external diff program will actually be pretty fast (at least
462 faster than having to compare the entire tree).
459 faster than having to compare the entire tree).
463
460
464 list of commands:
461 list of commands:
465
462
466 extdiff use external program to diff repository (or selected files)
463 extdiff use external program to diff repository (or selected files)
467
464
468 (use "hg help -v extdiff" to show built-in aliases and global options)
465 (use "hg help -v extdiff" to show built-in aliases and global options)
469
466
470
467
471
468
472
469
473
470
474
471
475
472
476
473
477
474
478
475
479
476
480
477
481
478
482
479
483
480
484
481
485 $ echo 'extdiff = !' >> $HGRCPATH
482 $ echo 'extdiff = !' >> $HGRCPATH
486
483
487 Test help topic with same name as extension
484 Test help topic with same name as extension
488
485
489 $ cat > multirevs.py <<EOF
486 $ cat > multirevs.py <<EOF
490 > from mercurial import cmdutil, commands
487 > from mercurial import cmdutil, commands
491 > cmdtable = {}
488 > cmdtable = {}
492 > command = cmdutil.command(cmdtable)
489 > command = cmdutil.command(cmdtable)
493 > """multirevs extension
490 > """multirevs extension
494 > Big multi-line module docstring."""
491 > Big multi-line module docstring."""
495 > @command('multirevs', [], 'ARG', norepo=True)
492 > @command('multirevs', [], 'ARG', norepo=True)
496 > def multirevs(ui, repo, arg, *args, **opts):
493 > def multirevs(ui, repo, arg, *args, **opts):
497 > """multirevs command"""
494 > """multirevs command"""
498 > pass
495 > pass
499 > EOF
496 > EOF
500 $ echo "multirevs = multirevs.py" >> $HGRCPATH
497 $ echo "multirevs = multirevs.py" >> $HGRCPATH
501
498
502 $ hg help multirevs
499 $ hg help multirevs
503 Specifying Multiple Revisions
500 Specifying Multiple Revisions
504 """""""""""""""""""""""""""""
501 """""""""""""""""""""""""""""
505
502
506 When Mercurial accepts more than one revision, they may be specified
503 When Mercurial accepts more than one revision, they may be specified
507 individually, or provided as a topologically continuous range, separated
504 individually, or provided as a topologically continuous range, separated
508 by the ":" character.
505 by the ":" character.
509
506
510 The syntax of range notation is [BEGIN]:[END], where BEGIN and END are
507 The syntax of range notation is [BEGIN]:[END], where BEGIN and END are
511 revision identifiers. Both BEGIN and END are optional. If BEGIN is not
508 revision identifiers. Both BEGIN and END are optional. If BEGIN is not
512 specified, it defaults to revision number 0. If END is not specified, it
509 specified, it defaults to revision number 0. If END is not specified, it
513 defaults to the tip. The range ":" thus means "all revisions".
510 defaults to the tip. The range ":" thus means "all revisions".
514
511
515 If BEGIN is greater than END, revisions are treated in reverse order.
512 If BEGIN is greater than END, revisions are treated in reverse order.
516
513
517 A range acts as a closed interval. This means that a range of 3:5 gives 3,
514 A range acts as a closed interval. This means that a range of 3:5 gives 3,
518 4 and 5. Similarly, a range of 9:6 gives 9, 8, 7, and 6.
515 4 and 5. Similarly, a range of 9:6 gives 9, 8, 7, and 6.
519
516
520 use "hg help -c multirevs" to see help for the multirevs command
517 use "hg help -c multirevs" to see help for the multirevs command
521
518
522
519
523
520
524
521
525
522
526
523
527 $ hg help -c multirevs
524 $ hg help -c multirevs
528 hg multirevs ARG
525 hg multirevs ARG
529
526
530 multirevs command
527 multirevs command
531
528
532 (some details hidden, use --verbose to show complete help)
529 (some details hidden, use --verbose to show complete help)
533
530
534
531
535
532
536 $ hg multirevs
533 $ hg multirevs
537 hg multirevs: invalid arguments
534 hg multirevs: invalid arguments
538 hg multirevs ARG
535 hg multirevs ARG
539
536
540 multirevs command
537 multirevs command
541
538
542 (use "hg multirevs -h" to show more help)
539 (use "hg multirevs -h" to show more help)
543 [255]
540 [255]
544
541
545
542
546
543
547 $ echo "multirevs = !" >> $HGRCPATH
544 $ echo "multirevs = !" >> $HGRCPATH
548
545
549 Issue811: Problem loading extensions twice (by site and by user)
546 Issue811: Problem loading extensions twice (by site and by user)
550
547
551 $ debugpath=`pwd`/debugissue811.py
548 $ debugpath=`pwd`/debugissue811.py
552 $ cat > debugissue811.py <<EOF
549 $ cat > debugissue811.py <<EOF
553 > '''show all loaded extensions
550 > '''show all loaded extensions
554 > '''
551 > '''
555 > from mercurial import cmdutil, commands, extensions
552 > from mercurial import cmdutil, commands, extensions
556 > cmdtable = {}
553 > cmdtable = {}
557 > command = cmdutil.command(cmdtable)
554 > command = cmdutil.command(cmdtable)
558 > @command('debugextensions', [], 'hg debugextensions', norepo=True)
555 > @command('debugextensions', [], 'hg debugextensions', norepo=True)
559 > def debugextensions(ui):
556 > def debugextensions(ui):
560 > "yet another debug command"
557 > "yet another debug command"
561 > ui.write("%s\n" % '\n'.join([x for x, y in extensions.extensions()]))
558 > ui.write("%s\n" % '\n'.join([x for x, y in extensions.extensions()]))
562 > EOF
559 > EOF
563 $ echo "debugissue811 = $debugpath" >> $HGRCPATH
560 $ echo "debugissue811 = $debugpath" >> $HGRCPATH
564 $ echo "mq=" >> $HGRCPATH
561 $ echo "mq=" >> $HGRCPATH
565 $ echo "strip=" >> $HGRCPATH
562 $ echo "strip=" >> $HGRCPATH
566 $ echo "hgext.mq=" >> $HGRCPATH
563 $ echo "hgext.mq=" >> $HGRCPATH
567 $ echo "hgext/mq=" >> $HGRCPATH
564 $ echo "hgext/mq=" >> $HGRCPATH
568
565
569 Show extensions:
566 Show extensions:
570 (note that mq force load strip, also checking it's not loaded twice)
567 (note that mq force load strip, also checking it's not loaded twice)
571
568
572 $ hg debugextensions
569 $ hg debugextensions
573 debugissue811
570 debugissue811
574 strip
571 strip
575 mq
572 mq
576
573
577 Disabled extension commands:
574 Disabled extension commands:
578
575
579 $ ORGHGRCPATH=$HGRCPATH
576 $ ORGHGRCPATH=$HGRCPATH
580 $ HGRCPATH=
577 $ HGRCPATH=
581 $ export HGRCPATH
578 $ export HGRCPATH
582 $ hg help email
579 $ hg help email
583 'email' is provided by the following extension:
580 'email' is provided by the following extension:
584
581
585 patchbomb command to send changesets as (a series of) patch emails
582 patchbomb command to send changesets as (a series of) patch emails
586
583
587 (use "hg help extensions" for information on enabling extensions)
584 (use "hg help extensions" for information on enabling extensions)
588
585
589
586
590 $ hg qdel
587 $ hg qdel
591 hg: unknown command 'qdel'
588 hg: unknown command 'qdel'
592 'qdelete' is provided by the following extension:
589 'qdelete' is provided by the following extension:
593
590
594 mq manage a stack of patches
591 mq manage a stack of patches
595
592
596 (use "hg help extensions" for information on enabling extensions)
593 (use "hg help extensions" for information on enabling extensions)
597 [255]
594 [255]
598
595
599
596
600 $ hg churn
597 $ hg churn
601 hg: unknown command 'churn'
598 hg: unknown command 'churn'
602 'churn' is provided by the following extension:
599 'churn' is provided by the following extension:
603
600
604 churn command to display statistics about repository history
601 churn command to display statistics about repository history
605
602
606 (use "hg help extensions" for information on enabling extensions)
603 (use "hg help extensions" for information on enabling extensions)
607 [255]
604 [255]
608
605
609
606
610
607
611 Disabled extensions:
608 Disabled extensions:
612
609
613 $ hg help churn
610 $ hg help churn
614 churn extension - command to display statistics about repository history
611 churn extension - command to display statistics about repository history
615
612
616 (use "hg help extensions" for information on enabling extensions)
613 (use "hg help extensions" for information on enabling extensions)
617
614
618 $ hg help patchbomb
615 $ hg help patchbomb
619 patchbomb extension - command to send changesets as (a series of) patch emails
616 patchbomb extension - command to send changesets as (a series of) patch emails
620
617
621 (use "hg help extensions" for information on enabling extensions)
618 (use "hg help extensions" for information on enabling extensions)
622
619
623
620
624 Broken disabled extension and command:
621 Broken disabled extension and command:
625
622
626 $ mkdir hgext
623 $ mkdir hgext
627 $ echo > hgext/__init__.py
624 $ echo > hgext/__init__.py
628 $ cat > hgext/broken.py <<EOF
625 $ cat > hgext/broken.py <<EOF
629 > "broken extension'
626 > "broken extension'
630 > EOF
627 > EOF
631 $ cat > path.py <<EOF
628 $ cat > path.py <<EOF
632 > import os, sys
629 > import os, sys
633 > sys.path.insert(0, os.environ['HGEXTPATH'])
630 > sys.path.insert(0, os.environ['HGEXTPATH'])
634 > EOF
631 > EOF
635 $ HGEXTPATH=`pwd`
632 $ HGEXTPATH=`pwd`
636 $ export HGEXTPATH
633 $ export HGEXTPATH
637
634
638 $ hg --config extensions.path=./path.py help broken
635 $ hg --config extensions.path=./path.py help broken
639 broken extension - (no help text available)
636 broken extension - (no help text available)
640
637
641 (use "hg help extensions" for information on enabling extensions)
638 (use "hg help extensions" for information on enabling extensions)
642
639
643
640
644 $ cat > hgext/forest.py <<EOF
641 $ cat > hgext/forest.py <<EOF
645 > cmdtable = None
642 > cmdtable = None
646 > EOF
643 > EOF
647 $ hg --config extensions.path=./path.py help foo > /dev/null
644 $ hg --config extensions.path=./path.py help foo > /dev/null
648 warning: error finding commands in $TESTTMP/hgext/forest.py (glob)
645 warning: error finding commands in $TESTTMP/hgext/forest.py (glob)
649 abort: no such help topic: foo
646 abort: no such help topic: foo
650 (try "hg help --keyword foo")
647 (try "hg help --keyword foo")
651 [255]
648 [255]
652
649
653 $ cat > throw.py <<EOF
650 $ cat > throw.py <<EOF
654 > from mercurial import cmdutil, commands
651 > from mercurial import cmdutil, commands
655 > cmdtable = {}
652 > cmdtable = {}
656 > command = cmdutil.command(cmdtable)
653 > command = cmdutil.command(cmdtable)
657 > class Bogon(Exception): pass
654 > class Bogon(Exception): pass
658 > @command('throw', [], 'hg throw', norepo=True)
655 > @command('throw', [], 'hg throw', norepo=True)
659 > def throw(ui, **opts):
656 > def throw(ui, **opts):
660 > """throws an exception"""
657 > """throws an exception"""
661 > raise Bogon()
658 > raise Bogon()
662 > EOF
659 > EOF
663 No declared supported version, extension complains:
660 No declared supported version, extension complains:
664 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
661 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
665 ** Unknown exception encountered with possibly-broken third-party extension throw
662 ** Unknown exception encountered with possibly-broken third-party extension throw
666 ** which supports versions unknown of Mercurial.
663 ** which supports versions unknown of Mercurial.
667 ** Please disable throw and try your action again.
664 ** Please disable throw and try your action again.
668 ** If that fixes the bug please report it to the extension author.
665 ** If that fixes the bug please report it to the extension author.
669 ** Python * (glob)
666 ** Python * (glob)
670 ** Mercurial Distributed SCM * (glob)
667 ** Mercurial Distributed SCM * (glob)
671 ** Extensions loaded: throw
668 ** Extensions loaded: throw
672 empty declaration of supported version, extension complains:
669 empty declaration of supported version, extension complains:
673 $ echo "testedwith = ''" >> throw.py
670 $ echo "testedwith = ''" >> throw.py
674 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
671 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
675 ** Unknown exception encountered with possibly-broken third-party extension throw
672 ** Unknown exception encountered with possibly-broken third-party extension throw
676 ** which supports versions unknown of Mercurial.
673 ** which supports versions unknown of Mercurial.
677 ** Please disable throw and try your action again.
674 ** Please disable throw and try your action again.
678 ** If that fixes the bug please report it to the extension author.
675 ** If that fixes the bug please report it to the extension author.
679 ** Python * (glob)
676 ** Python * (glob)
680 ** Mercurial Distributed SCM (*) (glob)
677 ** Mercurial Distributed SCM (*) (glob)
681 ** Extensions loaded: throw
678 ** Extensions loaded: throw
682 If the extension specifies a buglink, show that:
679 If the extension specifies a buglink, show that:
683 $ echo 'buglink = "http://example.com/bts"' >> throw.py
680 $ echo 'buglink = "http://example.com/bts"' >> throw.py
684 $ rm -f throw.pyc throw.pyo
681 $ rm -f throw.pyc throw.pyo
685 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
682 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
686 ** Unknown exception encountered with possibly-broken third-party extension throw
683 ** Unknown exception encountered with possibly-broken third-party extension throw
687 ** which supports versions unknown of Mercurial.
684 ** which supports versions unknown of Mercurial.
688 ** Please disable throw and try your action again.
685 ** Please disable throw and try your action again.
689 ** If that fixes the bug please report it to http://example.com/bts
686 ** If that fixes the bug please report it to http://example.com/bts
690 ** Python * (glob)
687 ** Python * (glob)
691 ** Mercurial Distributed SCM (*) (glob)
688 ** Mercurial Distributed SCM (*) (glob)
692 ** Extensions loaded: throw
689 ** Extensions loaded: throw
693 If the extensions declare outdated versions, accuse the older extension first:
690 If the extensions declare outdated versions, accuse the older extension first:
694 $ echo "from mercurial import util" >> older.py
691 $ echo "from mercurial import util" >> older.py
695 $ echo "util.version = lambda:'2.2'" >> older.py
692 $ echo "util.version = lambda:'2.2'" >> older.py
696 $ echo "testedwith = '1.9.3'" >> older.py
693 $ echo "testedwith = '1.9.3'" >> older.py
697 $ echo "testedwith = '2.1.1'" >> throw.py
694 $ echo "testedwith = '2.1.1'" >> throw.py
698 $ rm -f throw.pyc throw.pyo
695 $ rm -f throw.pyc throw.pyo
699 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
696 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
700 > throw 2>&1 | egrep '^\*\*'
697 > throw 2>&1 | egrep '^\*\*'
701 ** Unknown exception encountered with possibly-broken third-party extension older
698 ** Unknown exception encountered with possibly-broken third-party extension older
702 ** which supports versions 1.9.3 of Mercurial.
699 ** which supports versions 1.9.3 of Mercurial.
703 ** Please disable older and try your action again.
700 ** Please disable older and try your action again.
704 ** If that fixes the bug please report it to the extension author.
701 ** If that fixes the bug please report it to the extension author.
705 ** Python * (glob)
702 ** Python * (glob)
706 ** Mercurial Distributed SCM (version 2.2)
703 ** Mercurial Distributed SCM (version 2.2)
707 ** Extensions loaded: throw, older
704 ** Extensions loaded: throw, older
708 One extension only tested with older, one only with newer versions:
705 One extension only tested with older, one only with newer versions:
709 $ echo "util.version = lambda:'2.1.0'" >> older.py
706 $ echo "util.version = lambda:'2.1.0'" >> older.py
710 $ rm -f older.pyc older.pyo
707 $ rm -f older.pyc older.pyo
711 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
708 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
712 > throw 2>&1 | egrep '^\*\*'
709 > throw 2>&1 | egrep '^\*\*'
713 ** Unknown exception encountered with possibly-broken third-party extension older
710 ** Unknown exception encountered with possibly-broken third-party extension older
714 ** which supports versions 1.9.3 of Mercurial.
711 ** which supports versions 1.9.3 of Mercurial.
715 ** Please disable older and try your action again.
712 ** Please disable older and try your action again.
716 ** If that fixes the bug please report it to the extension author.
713 ** If that fixes the bug please report it to the extension author.
717 ** Python * (glob)
714 ** Python * (glob)
718 ** Mercurial Distributed SCM (version 2.1.0)
715 ** Mercurial Distributed SCM (version 2.1.0)
719 ** Extensions loaded: throw, older
716 ** Extensions loaded: throw, older
720 Older extension is tested with current version, the other only with newer:
717 Older extension is tested with current version, the other only with newer:
721 $ echo "util.version = lambda:'1.9.3'" >> older.py
718 $ echo "util.version = lambda:'1.9.3'" >> older.py
722 $ rm -f older.pyc older.pyo
719 $ rm -f older.pyc older.pyo
723 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
720 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
724 > throw 2>&1 | egrep '^\*\*'
721 > throw 2>&1 | egrep '^\*\*'
725 ** Unknown exception encountered with possibly-broken third-party extension throw
722 ** Unknown exception encountered with possibly-broken third-party extension throw
726 ** which supports versions 2.1.1 of Mercurial.
723 ** which supports versions 2.1.1 of Mercurial.
727 ** Please disable throw and try your action again.
724 ** Please disable throw and try your action again.
728 ** If that fixes the bug please report it to http://example.com/bts
725 ** If that fixes the bug please report it to http://example.com/bts
729 ** Python * (glob)
726 ** Python * (glob)
730 ** Mercurial Distributed SCM (version 1.9.3)
727 ** Mercurial Distributed SCM (version 1.9.3)
731 ** Extensions loaded: throw, older
728 ** Extensions loaded: throw, older
732
729
733 Declare the version as supporting this hg version, show regular bts link:
730 Declare the version as supporting this hg version, show regular bts link:
734 $ hgver=`$PYTHON -c 'from mercurial import util; print util.version().split("+")[0]'`
731 $ hgver=`$PYTHON -c 'from mercurial import util; print util.version().split("+")[0]'`
735 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
732 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
736 $ rm -f throw.pyc throw.pyo
733 $ rm -f throw.pyc throw.pyo
737 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
734 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
738 ** unknown exception encountered, please report by visiting
735 ** unknown exception encountered, please report by visiting
739 ** http://mercurial.selenic.com/wiki/BugTracker
736 ** http://mercurial.selenic.com/wiki/BugTracker
740 ** Python * (glob)
737 ** Python * (glob)
741 ** Mercurial Distributed SCM (*) (glob)
738 ** Mercurial Distributed SCM (*) (glob)
742 ** Extensions loaded: throw
739 ** Extensions loaded: throw
743
740
744 Test version number support in 'hg version':
741 Test version number support in 'hg version':
745 $ echo '__version__ = (1, 2, 3)' >> throw.py
742 $ echo '__version__ = (1, 2, 3)' >> throw.py
746 $ rm -f throw.pyc throw.pyo
743 $ rm -f throw.pyc throw.pyo
747 $ hg version -v
744 $ hg version -v
748 Mercurial Distributed SCM (version *) (glob)
745 Mercurial Distributed SCM (version *) (glob)
749 (see http://mercurial.selenic.com for more information)
746 (see http://mercurial.selenic.com for more information)
750
747
751 Copyright (C) 2005-* Matt Mackall and others (glob)
748 Copyright (C) 2005-* Matt Mackall and others (glob)
752 This is free software; see the source for copying conditions. There is NO
749 This is free software; see the source for copying conditions. There is NO
753 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
750 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
754
751
755 Enabled extensions:
752 Enabled extensions:
756
753
757
754
758 $ hg version -v --config extensions.throw=throw.py
755 $ hg version -v --config extensions.throw=throw.py
759 Mercurial Distributed SCM (version *) (glob)
756 Mercurial Distributed SCM (version *) (glob)
760 (see http://mercurial.selenic.com for more information)
757 (see http://mercurial.selenic.com for more information)
761
758
762 Copyright (C) 2005-* Matt Mackall and others (glob)
759 Copyright (C) 2005-* Matt Mackall and others (glob)
763 This is free software; see the source for copying conditions. There is NO
760 This is free software; see the source for copying conditions. There is NO
764 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
761 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
765
762
766 Enabled extensions:
763 Enabled extensions:
767
764
768 throw 1.2.3
765 throw 1.2.3
769 $ echo 'getversion = lambda: "1.twentythree"' >> throw.py
766 $ echo 'getversion = lambda: "1.twentythree"' >> throw.py
770 $ rm -f throw.pyc throw.pyo
767 $ rm -f throw.pyc throw.pyo
771 $ hg version -v --config extensions.throw=throw.py
768 $ hg version -v --config extensions.throw=throw.py
772 Mercurial Distributed SCM (version *) (glob)
769 Mercurial Distributed SCM (version *) (glob)
773 (see http://mercurial.selenic.com for more information)
770 (see http://mercurial.selenic.com for more information)
774
771
775 Copyright (C) 2005-* Matt Mackall and others (glob)
772 Copyright (C) 2005-* Matt Mackall and others (glob)
776 This is free software; see the source for copying conditions. There is NO
773 This is free software; see the source for copying conditions. There is NO
777 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
774 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
778
775
779 Enabled extensions:
776 Enabled extensions:
780
777
781 throw 1.twentythree
778 throw 1.twentythree
782
779
783 Restore HGRCPATH
780 Restore HGRCPATH
784
781
785 $ HGRCPATH=$ORGHGRCPATH
782 $ HGRCPATH=$ORGHGRCPATH
786 $ export HGRCPATH
783 $ export HGRCPATH
787
784
788 Commands handling multiple repositories at a time should invoke only
785 Commands handling multiple repositories at a time should invoke only
789 "reposetup()" of extensions enabling in the target repository.
786 "reposetup()" of extensions enabling in the target repository.
790
787
791 $ mkdir reposetup-test
788 $ mkdir reposetup-test
792 $ cd reposetup-test
789 $ cd reposetup-test
793
790
794 $ cat > $TESTTMP/reposetuptest.py <<EOF
791 $ cat > $TESTTMP/reposetuptest.py <<EOF
795 > from mercurial import extensions
792 > from mercurial import extensions
796 > def reposetup(ui, repo):
793 > def reposetup(ui, repo):
797 > ui.write('reposetup() for %s\n' % (repo.root))
794 > ui.write('reposetup() for %s\n' % (repo.root))
798 > EOF
795 > EOF
799 $ hg init src
796 $ hg init src
800 $ echo a > src/a
797 $ echo a > src/a
801 $ hg -R src commit -Am '#0 at src/a'
798 $ hg -R src commit -Am '#0 at src/a'
802 adding a
799 adding a
803 $ echo '[extensions]' >> src/.hg/hgrc
800 $ echo '[extensions]' >> src/.hg/hgrc
804 $ echo '# enable extension locally' >> src/.hg/hgrc
801 $ echo '# enable extension locally' >> src/.hg/hgrc
805 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
802 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
806 $ hg -R src status
803 $ hg -R src status
807 reposetup() for $TESTTMP/reposetup-test/src (glob)
804 reposetup() for $TESTTMP/reposetup-test/src (glob)
808
805
809 $ hg clone -U src clone-dst1
806 $ hg clone -U src clone-dst1
810 reposetup() for $TESTTMP/reposetup-test/src (glob)
807 reposetup() for $TESTTMP/reposetup-test/src (glob)
811 $ hg init push-dst1
808 $ hg init push-dst1
812 $ hg -q -R src push push-dst1
809 $ hg -q -R src push push-dst1
813 reposetup() for $TESTTMP/reposetup-test/src (glob)
810 reposetup() for $TESTTMP/reposetup-test/src (glob)
814 $ hg init pull-src1
811 $ hg init pull-src1
815 $ hg -q -R pull-src1 pull src
812 $ hg -q -R pull-src1 pull src
816 reposetup() for $TESTTMP/reposetup-test/src (glob)
813 reposetup() for $TESTTMP/reposetup-test/src (glob)
817
814
818 $ echo '[extensions]' >> $HGRCPATH
815 $ echo '[extensions]' >> $HGRCPATH
819 $ echo '# disable extension globally and explicitly' >> $HGRCPATH
816 $ echo '# disable extension globally and explicitly' >> $HGRCPATH
820 $ echo 'reposetuptest = !' >> $HGRCPATH
817 $ echo 'reposetuptest = !' >> $HGRCPATH
821 $ hg clone -U src clone-dst2
818 $ hg clone -U src clone-dst2
822 reposetup() for $TESTTMP/reposetup-test/src (glob)
819 reposetup() for $TESTTMP/reposetup-test/src (glob)
823 $ hg init push-dst2
820 $ hg init push-dst2
824 $ hg -q -R src push push-dst2
821 $ hg -q -R src push push-dst2
825 reposetup() for $TESTTMP/reposetup-test/src (glob)
822 reposetup() for $TESTTMP/reposetup-test/src (glob)
826 $ hg init pull-src2
823 $ hg init pull-src2
827 $ hg -q -R pull-src2 pull src
824 $ hg -q -R pull-src2 pull src
828 reposetup() for $TESTTMP/reposetup-test/src (glob)
825 reposetup() for $TESTTMP/reposetup-test/src (glob)
829
826
830 $ echo '[extensions]' >> $HGRCPATH
827 $ echo '[extensions]' >> $HGRCPATH
831 $ echo '# enable extension globally' >> $HGRCPATH
828 $ echo '# enable extension globally' >> $HGRCPATH
832 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> $HGRCPATH
829 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> $HGRCPATH
833 $ hg clone -U src clone-dst3
830 $ hg clone -U src clone-dst3
834 reposetup() for $TESTTMP/reposetup-test/src (glob)
831 reposetup() for $TESTTMP/reposetup-test/src (glob)
835 reposetup() for $TESTTMP/reposetup-test/clone-dst3 (glob)
832 reposetup() for $TESTTMP/reposetup-test/clone-dst3 (glob)
836 $ hg init push-dst3
833 $ hg init push-dst3
837 reposetup() for $TESTTMP/reposetup-test/push-dst3 (glob)
834 reposetup() for $TESTTMP/reposetup-test/push-dst3 (glob)
838 $ hg -q -R src push push-dst3
835 $ hg -q -R src push push-dst3
839 reposetup() for $TESTTMP/reposetup-test/src (glob)
836 reposetup() for $TESTTMP/reposetup-test/src (glob)
840 reposetup() for $TESTTMP/reposetup-test/push-dst3 (glob)
837 reposetup() for $TESTTMP/reposetup-test/push-dst3 (glob)
841 $ hg init pull-src3
838 $ hg init pull-src3
842 reposetup() for $TESTTMP/reposetup-test/pull-src3 (glob)
839 reposetup() for $TESTTMP/reposetup-test/pull-src3 (glob)
843 $ hg -q -R pull-src3 pull src
840 $ hg -q -R pull-src3 pull src
844 reposetup() for $TESTTMP/reposetup-test/pull-src3 (glob)
841 reposetup() for $TESTTMP/reposetup-test/pull-src3 (glob)
845 reposetup() for $TESTTMP/reposetup-test/src (glob)
842 reposetup() for $TESTTMP/reposetup-test/src (glob)
846
843
847 $ echo '[extensions]' >> src/.hg/hgrc
844 $ echo '[extensions]' >> src/.hg/hgrc
848 $ echo '# disable extension locally' >> src/.hg/hgrc
845 $ echo '# disable extension locally' >> src/.hg/hgrc
849 $ echo 'reposetuptest = !' >> src/.hg/hgrc
846 $ echo 'reposetuptest = !' >> src/.hg/hgrc
850 $ hg clone -U src clone-dst4
847 $ hg clone -U src clone-dst4
851 reposetup() for $TESTTMP/reposetup-test/clone-dst4 (glob)
848 reposetup() for $TESTTMP/reposetup-test/clone-dst4 (glob)
852 $ hg init push-dst4
849 $ hg init push-dst4
853 reposetup() for $TESTTMP/reposetup-test/push-dst4 (glob)
850 reposetup() for $TESTTMP/reposetup-test/push-dst4 (glob)
854 $ hg -q -R src push push-dst4
851 $ hg -q -R src push push-dst4
855 reposetup() for $TESTTMP/reposetup-test/push-dst4 (glob)
852 reposetup() for $TESTTMP/reposetup-test/push-dst4 (glob)
856 $ hg init pull-src4
853 $ hg init pull-src4
857 reposetup() for $TESTTMP/reposetup-test/pull-src4 (glob)
854 reposetup() for $TESTTMP/reposetup-test/pull-src4 (glob)
858 $ hg -q -R pull-src4 pull src
855 $ hg -q -R pull-src4 pull src
859 reposetup() for $TESTTMP/reposetup-test/pull-src4 (glob)
856 reposetup() for $TESTTMP/reposetup-test/pull-src4 (glob)
860
857
861 disabling in command line overlays with all configuration
858 disabling in command line overlays with all configuration
862 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
859 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
863 $ hg --config extensions.reposetuptest=! init push-dst5
860 $ hg --config extensions.reposetuptest=! init push-dst5
864 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
861 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
865 $ hg --config extensions.reposetuptest=! init pull-src5
862 $ hg --config extensions.reposetuptest=! init pull-src5
866 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
863 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
867
864
868 $ echo '[extensions]' >> $HGRCPATH
865 $ echo '[extensions]' >> $HGRCPATH
869 $ echo '# disable extension globally and explicitly' >> $HGRCPATH
866 $ echo '# disable extension globally and explicitly' >> $HGRCPATH
870 $ echo 'reposetuptest = !' >> $HGRCPATH
867 $ echo 'reposetuptest = !' >> $HGRCPATH
871 $ hg init parent
868 $ hg init parent
872 $ hg init parent/sub1
869 $ hg init parent/sub1
873 $ echo 1 > parent/sub1/1
870 $ echo 1 > parent/sub1/1
874 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
871 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
875 adding 1
872 adding 1
876 $ hg init parent/sub2
873 $ hg init parent/sub2
877 $ hg init parent/sub2/sub21
874 $ hg init parent/sub2/sub21
878 $ echo 21 > parent/sub2/sub21/21
875 $ echo 21 > parent/sub2/sub21/21
879 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
876 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
880 adding 21
877 adding 21
881 $ cat > parent/sub2/.hgsub <<EOF
878 $ cat > parent/sub2/.hgsub <<EOF
882 > sub21 = sub21
879 > sub21 = sub21
883 > EOF
880 > EOF
884 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
881 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
885 adding .hgsub
882 adding .hgsub
886 $ hg init parent/sub3
883 $ hg init parent/sub3
887 $ echo 3 > parent/sub3/3
884 $ echo 3 > parent/sub3/3
888 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
885 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
889 adding 3
886 adding 3
890 $ cat > parent/.hgsub <<EOF
887 $ cat > parent/.hgsub <<EOF
891 > sub1 = sub1
888 > sub1 = sub1
892 > sub2 = sub2
889 > sub2 = sub2
893 > sub3 = sub3
890 > sub3 = sub3
894 > EOF
891 > EOF
895 $ hg -R parent commit -Am '#0 at parent'
892 $ hg -R parent commit -Am '#0 at parent'
896 adding .hgsub
893 adding .hgsub
897 $ echo '[extensions]' >> parent/.hg/hgrc
894 $ echo '[extensions]' >> parent/.hg/hgrc
898 $ echo '# enable extension locally' >> parent/.hg/hgrc
895 $ echo '# enable extension locally' >> parent/.hg/hgrc
899 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
896 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
900 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
897 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
901 $ hg -R parent status -S -A
898 $ hg -R parent status -S -A
902 reposetup() for $TESTTMP/reposetup-test/parent (glob)
899 reposetup() for $TESTTMP/reposetup-test/parent (glob)
903 reposetup() for $TESTTMP/reposetup-test/parent/sub2 (glob)
900 reposetup() for $TESTTMP/reposetup-test/parent/sub2 (glob)
904 C .hgsub
901 C .hgsub
905 C .hgsubstate
902 C .hgsubstate
906 C sub1/1
903 C sub1/1
907 C sub2/.hgsub
904 C sub2/.hgsub
908 C sub2/.hgsubstate
905 C sub2/.hgsubstate
909 C sub2/sub21/21
906 C sub2/sub21/21
910 C sub3/3
907 C sub3/3
911
908
912 $ cd ..
909 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now