##// END OF EJS Templates
extdiff: factor out list of common options
Yuya Nishihara -
r27680:c750245c default
parent child Browse files
Show More
@@ -1,372 +1,374
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 meld, runs meld (no need to name twice). If
26 # add new command called meld, runs meld (no need to name twice). If
27 # 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]
28 # will be used, if available
28 # will be used, if available
29 meld =
29 meld =
30
30
31 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
31 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
32 # (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
33 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
33 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
34 # your .vimrc
34 # your .vimrc
35 vimdiff = gvim -f "+next" \\
35 vimdiff = gvim -f "+next" \\
36 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
36 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
37
37
38 Tool arguments can include variables that are expanded at runtime::
38 Tool arguments can include variables that are expanded at runtime::
39
39
40 $parent1, $plabel1 - filename, descriptive label of first parent
40 $parent1, $plabel1 - filename, descriptive label of first parent
41 $child, $clabel - filename, descriptive label of child revision
41 $child, $clabel - filename, descriptive label of child revision
42 $parent2, $plabel2 - filename, descriptive label of second parent
42 $parent2, $plabel2 - filename, descriptive label of second parent
43 $root - repository root
43 $root - repository root
44 $parent is an alias for $parent1.
44 $parent is an alias for $parent1.
45
45
46 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]
47 sections for diff tool arguments, when none are specified in [extdiff].
47 sections for diff tool arguments, when none are specified in [extdiff].
48
48
49 ::
49 ::
50
50
51 [extdiff]
51 [extdiff]
52 kdiff3 =
52 kdiff3 =
53
53
54 [diff-tools]
54 [diff-tools]
55 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
55 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
56
56
57 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
58 :hg:`diff` command. The extdiff extension makes snapshots of only
58 :hg:`diff` command. The extdiff extension makes snapshots of only
59 needed files, so running the external diff program will actually be
59 needed files, so running the external diff program will actually be
60 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).
61 '''
61 '''
62
62
63 from mercurial.i18n import _
63 from mercurial.i18n import _
64 from mercurial.node import short, nullid
64 from mercurial.node import short, nullid
65 from mercurial import cmdutil, scmutil, util, commands, encoding, filemerge
65 from mercurial import cmdutil, scmutil, util, commands, encoding, filemerge
66 from mercurial import archival, error
66 from mercurial import archival, error
67 import os, shlex, shutil, tempfile, re
67 import os, shlex, shutil, tempfile, re
68
68
69 cmdtable = {}
69 cmdtable = {}
70 command = cmdutil.command(cmdtable)
70 command = cmdutil.command(cmdtable)
71 # Note for extension authors: ONLY specify testedwith = 'internal' for
71 # Note for extension authors: ONLY specify testedwith = 'internal' for
72 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
72 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
73 # be specifying the version(s) of Mercurial they are tested with, or
73 # be specifying the version(s) of Mercurial they are tested with, or
74 # leave the attribute unspecified.
74 # leave the attribute unspecified.
75 testedwith = 'internal'
75 testedwith = 'internal'
76
76
77 def snapshot(ui, repo, files, node, tmproot, listsubrepos):
77 def snapshot(ui, repo, files, node, tmproot, listsubrepos):
78 '''snapshot files as of some revision
78 '''snapshot files as of some revision
79 if not using snapshot, -I/-X does not work and recursive diff
79 if not using snapshot, -I/-X does not work and recursive diff
80 in tools like kdiff3 and meld displays too many files.'''
80 in tools like kdiff3 and meld displays too many files.'''
81 dirname = os.path.basename(repo.root)
81 dirname = os.path.basename(repo.root)
82 if dirname == "":
82 if dirname == "":
83 dirname = "root"
83 dirname = "root"
84 if node is not None:
84 if node is not None:
85 dirname = '%s.%s' % (dirname, short(node))
85 dirname = '%s.%s' % (dirname, short(node))
86 base = os.path.join(tmproot, dirname)
86 base = os.path.join(tmproot, dirname)
87 os.mkdir(base)
87 os.mkdir(base)
88 fns_and_mtime = []
88 fns_and_mtime = []
89
89
90 if node is not None:
90 if node is not None:
91 ui.note(_('making snapshot of %d files from rev %s\n') %
91 ui.note(_('making snapshot of %d files from rev %s\n') %
92 (len(files), short(node)))
92 (len(files), short(node)))
93 else:
93 else:
94 ui.note(_('making snapshot of %d files from working directory\n') %
94 ui.note(_('making snapshot of %d files from working directory\n') %
95 (len(files)))
95 (len(files)))
96
96
97 if files:
97 if files:
98 repo.ui.setconfig("ui", "archivemeta", False)
98 repo.ui.setconfig("ui", "archivemeta", False)
99
99
100 archival.archive(repo, base, node, 'files',
100 archival.archive(repo, base, node, 'files',
101 matchfn=scmutil.matchfiles(repo, files),
101 matchfn=scmutil.matchfiles(repo, files),
102 subrepos=listsubrepos)
102 subrepos=listsubrepos)
103
103
104 for fn in sorted(files):
104 for fn in sorted(files):
105 wfn = util.pconvert(fn)
105 wfn = util.pconvert(fn)
106 ui.note(' %s\n' % wfn)
106 ui.note(' %s\n' % wfn)
107
107
108 if node is None:
108 if node is None:
109 dest = os.path.join(base, wfn)
109 dest = os.path.join(base, wfn)
110
110
111 fns_and_mtime.append((dest, repo.wjoin(fn),
111 fns_and_mtime.append((dest, repo.wjoin(fn),
112 os.lstat(dest).st_mtime))
112 os.lstat(dest).st_mtime))
113 return dirname, fns_and_mtime
113 return dirname, fns_and_mtime
114
114
115 def dodiff(ui, repo, cmdline, pats, opts):
115 def dodiff(ui, repo, cmdline, pats, opts):
116 '''Do the actual diff:
116 '''Do the actual diff:
117
117
118 - copy to a temp structure if diffing 2 internal revisions
118 - copy to a temp structure if diffing 2 internal revisions
119 - copy to a temp structure if diffing working revision with
119 - copy to a temp structure if diffing working revision with
120 another one and more than 1 file is changed
120 another one and more than 1 file is changed
121 - just invoke the diff for a single file in the working dir
121 - just invoke the diff for a single file in the working dir
122 '''
122 '''
123
123
124 revs = opts.get('rev')
124 revs = opts.get('rev')
125 change = opts.get('change')
125 change = opts.get('change')
126 do3way = '$parent2' in cmdline
126 do3way = '$parent2' in cmdline
127
127
128 if revs and change:
128 if revs and change:
129 msg = _('cannot specify --rev and --change at the same time')
129 msg = _('cannot specify --rev and --change at the same time')
130 raise error.Abort(msg)
130 raise error.Abort(msg)
131 elif change:
131 elif change:
132 node2 = scmutil.revsingle(repo, change, None).node()
132 node2 = scmutil.revsingle(repo, change, None).node()
133 node1a, node1b = repo.changelog.parents(node2)
133 node1a, node1b = repo.changelog.parents(node2)
134 else:
134 else:
135 node1a, node2 = scmutil.revpair(repo, revs)
135 node1a, node2 = scmutil.revpair(repo, revs)
136 if not revs:
136 if not revs:
137 node1b = repo.dirstate.p2()
137 node1b = repo.dirstate.p2()
138 else:
138 else:
139 node1b = nullid
139 node1b = nullid
140
140
141 # Disable 3-way merge if there is only one parent
141 # Disable 3-way merge if there is only one parent
142 if do3way:
142 if do3way:
143 if node1b == nullid:
143 if node1b == nullid:
144 do3way = False
144 do3way = False
145
145
146 subrepos=opts.get('subrepos')
146 subrepos=opts.get('subrepos')
147
147
148 matcher = scmutil.match(repo[node2], pats, opts)
148 matcher = scmutil.match(repo[node2], pats, opts)
149
149
150 if opts.get('patch'):
150 if opts.get('patch'):
151 if subrepos:
151 if subrepos:
152 raise error.Abort(_('--patch cannot be used with --subrepos'))
152 raise error.Abort(_('--patch cannot be used with --subrepos'))
153 if node2 is None:
153 if node2 is None:
154 raise error.Abort(_('--patch requires two revisions'))
154 raise error.Abort(_('--patch requires two revisions'))
155 else:
155 else:
156 mod_a, add_a, rem_a = map(set, repo.status(node1a, node2, matcher,
156 mod_a, add_a, rem_a = map(set, repo.status(node1a, node2, matcher,
157 listsubrepos=subrepos)[:3])
157 listsubrepos=subrepos)[:3])
158 if do3way:
158 if do3way:
159 mod_b, add_b, rem_b = map(set,
159 mod_b, add_b, rem_b = map(set,
160 repo.status(node1b, node2, matcher,
160 repo.status(node1b, node2, matcher,
161 listsubrepos=subrepos)[:3])
161 listsubrepos=subrepos)[:3])
162 else:
162 else:
163 mod_b, add_b, rem_b = set(), set(), set()
163 mod_b, add_b, rem_b = set(), set(), set()
164 modadd = mod_a | add_a | mod_b | add_b
164 modadd = mod_a | add_a | mod_b | add_b
165 common = modadd | rem_a | rem_b
165 common = modadd | rem_a | rem_b
166 if not common:
166 if not common:
167 return 0
167 return 0
168
168
169 tmproot = tempfile.mkdtemp(prefix='extdiff.')
169 tmproot = tempfile.mkdtemp(prefix='extdiff.')
170 try:
170 try:
171 if not opts.get('patch'):
171 if not opts.get('patch'):
172 # Always make a copy of node1a (and node1b, if applicable)
172 # Always make a copy of node1a (and node1b, if applicable)
173 dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a)
173 dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a)
174 dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot,
174 dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot,
175 subrepos)[0]
175 subrepos)[0]
176 rev1a = '@%d' % repo[node1a].rev()
176 rev1a = '@%d' % repo[node1a].rev()
177 if do3way:
177 if do3way:
178 dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b)
178 dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b)
179 dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot,
179 dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot,
180 subrepos)[0]
180 subrepos)[0]
181 rev1b = '@%d' % repo[node1b].rev()
181 rev1b = '@%d' % repo[node1b].rev()
182 else:
182 else:
183 dir1b = None
183 dir1b = None
184 rev1b = ''
184 rev1b = ''
185
185
186 fns_and_mtime = []
186 fns_and_mtime = []
187
187
188 # If node2 in not the wc or there is >1 change, copy it
188 # If node2 in not the wc or there is >1 change, copy it
189 dir2root = ''
189 dir2root = ''
190 rev2 = ''
190 rev2 = ''
191 if node2:
191 if node2:
192 dir2 = snapshot(ui, repo, modadd, node2, tmproot, subrepos)[0]
192 dir2 = snapshot(ui, repo, modadd, node2, tmproot, subrepos)[0]
193 rev2 = '@%d' % repo[node2].rev()
193 rev2 = '@%d' % repo[node2].rev()
194 elif len(common) > 1:
194 elif len(common) > 1:
195 #we only actually need to get the files to copy back to
195 #we only actually need to get the files to copy back to
196 #the working dir in this case (because the other cases
196 #the working dir in this case (because the other cases
197 #are: diffing 2 revisions or single file -- in which case
197 #are: diffing 2 revisions or single file -- in which case
198 #the file is already directly passed to the diff tool).
198 #the file is already directly passed to the diff tool).
199 dir2, fns_and_mtime = snapshot(ui, repo, modadd, None, tmproot,
199 dir2, fns_and_mtime = snapshot(ui, repo, modadd, None, tmproot,
200 subrepos)
200 subrepos)
201 else:
201 else:
202 # This lets the diff tool open the changed file directly
202 # This lets the diff tool open the changed file directly
203 dir2 = ''
203 dir2 = ''
204 dir2root = repo.root
204 dir2root = repo.root
205
205
206 label1a = rev1a
206 label1a = rev1a
207 label1b = rev1b
207 label1b = rev1b
208 label2 = rev2
208 label2 = rev2
209
209
210 # If only one change, diff the files instead of the directories
210 # If only one change, diff the files instead of the directories
211 # Handle bogus modifies correctly by checking if the files exist
211 # Handle bogus modifies correctly by checking if the files exist
212 if len(common) == 1:
212 if len(common) == 1:
213 common_file = util.localpath(common.pop())
213 common_file = util.localpath(common.pop())
214 dir1a = os.path.join(tmproot, dir1a, common_file)
214 dir1a = os.path.join(tmproot, dir1a, common_file)
215 label1a = common_file + rev1a
215 label1a = common_file + rev1a
216 if not os.path.isfile(dir1a):
216 if not os.path.isfile(dir1a):
217 dir1a = os.devnull
217 dir1a = os.devnull
218 if do3way:
218 if do3way:
219 dir1b = os.path.join(tmproot, dir1b, common_file)
219 dir1b = os.path.join(tmproot, dir1b, common_file)
220 label1b = common_file + rev1b
220 label1b = common_file + rev1b
221 if not os.path.isfile(dir1b):
221 if not os.path.isfile(dir1b):
222 dir1b = os.devnull
222 dir1b = os.devnull
223 dir2 = os.path.join(dir2root, dir2, common_file)
223 dir2 = os.path.join(dir2root, dir2, common_file)
224 label2 = common_file + rev2
224 label2 = common_file + rev2
225 else:
225 else:
226 template = 'hg-%h.patch'
226 template = 'hg-%h.patch'
227 cmdutil.export(repo, [repo[node1a].rev(), repo[node2].rev()],
227 cmdutil.export(repo, [repo[node1a].rev(), repo[node2].rev()],
228 template=repo.vfs.reljoin(tmproot, template),
228 template=repo.vfs.reljoin(tmproot, template),
229 match=matcher)
229 match=matcher)
230 label1a = cmdutil.makefilename(repo, template, node1a)
230 label1a = cmdutil.makefilename(repo, template, node1a)
231 label2 = cmdutil.makefilename(repo, template, node2)
231 label2 = cmdutil.makefilename(repo, template, node2)
232 dir1a = repo.vfs.reljoin(tmproot, label1a)
232 dir1a = repo.vfs.reljoin(tmproot, label1a)
233 dir2 = repo.vfs.reljoin(tmproot, label2)
233 dir2 = repo.vfs.reljoin(tmproot, label2)
234 dir1b = None
234 dir1b = None
235 label1b = None
235 label1b = None
236 fns_and_mtime = []
236 fns_and_mtime = []
237
237
238 # Function to quote file/dir names in the argument string.
238 # Function to quote file/dir names in the argument string.
239 # When not operating in 3-way mode, an empty string is
239 # When not operating in 3-way mode, an empty string is
240 # returned for parent2
240 # returned for parent2
241 replace = {'parent': dir1a, 'parent1': dir1a, 'parent2': dir1b,
241 replace = {'parent': dir1a, 'parent1': dir1a, 'parent2': dir1b,
242 'plabel1': label1a, 'plabel2': label1b,
242 'plabel1': label1a, 'plabel2': label1b,
243 'clabel': label2, 'child': dir2,
243 'clabel': label2, 'child': dir2,
244 'root': repo.root}
244 'root': repo.root}
245 def quote(match):
245 def quote(match):
246 pre = match.group(2)
246 pre = match.group(2)
247 key = match.group(3)
247 key = match.group(3)
248 if not do3way and key == 'parent2':
248 if not do3way and key == 'parent2':
249 return pre
249 return pre
250 return pre + util.shellquote(replace[key])
250 return pre + util.shellquote(replace[key])
251
251
252 # Match parent2 first, so 'parent1?' will match both parent1 and parent
252 # Match parent2 first, so 'parent1?' will match both parent1 and parent
253 regex = (r'''(['"]?)([^\s'"$]*)'''
253 regex = (r'''(['"]?)([^\s'"$]*)'''
254 r'\$(parent2|parent1?|child|plabel1|plabel2|clabel|root)\1')
254 r'\$(parent2|parent1?|child|plabel1|plabel2|clabel|root)\1')
255 if not do3way and not re.search(regex, cmdline):
255 if not do3way and not re.search(regex, cmdline):
256 cmdline += ' $parent1 $child'
256 cmdline += ' $parent1 $child'
257 cmdline = re.sub(regex, quote, cmdline)
257 cmdline = re.sub(regex, quote, cmdline)
258
258
259 ui.debug('running %r in %s\n' % (cmdline, tmproot))
259 ui.debug('running %r in %s\n' % (cmdline, tmproot))
260 ui.system(cmdline, cwd=tmproot)
260 ui.system(cmdline, cwd=tmproot)
261
261
262 for copy_fn, working_fn, mtime in fns_and_mtime:
262 for copy_fn, working_fn, mtime in fns_and_mtime:
263 if os.lstat(copy_fn).st_mtime != mtime:
263 if os.lstat(copy_fn).st_mtime != mtime:
264 ui.debug('file changed while diffing. '
264 ui.debug('file changed while diffing. '
265 'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn))
265 'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn))
266 util.copyfile(copy_fn, working_fn)
266 util.copyfile(copy_fn, working_fn)
267
267
268 return 1
268 return 1
269 finally:
269 finally:
270 ui.note(_('cleaning up temp directory\n'))
270 ui.note(_('cleaning up temp directory\n'))
271 shutil.rmtree(tmproot)
271 shutil.rmtree(tmproot)
272
272
273 @command('extdiff',
273 extdiffopts = [
274 [('p', 'program', '',
275 _('comparison program to run'), _('CMD')),
276 ('o', 'option', [],
274 ('o', 'option', [],
277 _('pass option to comparison program'), _('OPT')),
275 _('pass option to comparison program'), _('OPT')),
278 ('r', 'rev', [], _('revision'), _('REV')),
276 ('r', 'rev', [], _('revision'), _('REV')),
279 ('c', 'change', '', _('change made by revision'), _('REV')),
277 ('c', 'change', '', _('change made by revision'), _('REV')),
280 ('', 'patch', None, _('compare patches for two revisions'))
278 ('', 'patch', None, _('compare patches for two revisions'))
281 ] + commands.walkopts + commands.subrepoopts,
279 ] + commands.walkopts + commands.subrepoopts
280
281 @command('extdiff',
282 [('p', 'program', '', _('comparison program to run'), _('CMD')),
283 ] + extdiffopts,
282 _('hg extdiff [OPT]... [FILE]...'),
284 _('hg extdiff [OPT]... [FILE]...'),
283 inferrepo=True)
285 inferrepo=True)
284 def extdiff(ui, repo, *pats, **opts):
286 def extdiff(ui, repo, *pats, **opts):
285 '''use external program to diff repository (or selected files)
287 '''use external program to diff repository (or selected files)
286
288
287 Show differences between revisions for the specified files, using
289 Show differences between revisions for the specified files, using
288 an external program. The default program used is diff, with
290 an external program. The default program used is diff, with
289 default options "-Npru".
291 default options "-Npru".
290
292
291 To select a different program, use the -p/--program option. The
293 To select a different program, use the -p/--program option. The
292 program will be passed the names of two directories to compare. To
294 program will be passed the names of two directories to compare. To
293 pass additional options to the program, use -o/--option. These
295 pass additional options to the program, use -o/--option. These
294 will be passed before the names of the directories to compare.
296 will be passed before the names of the directories to compare.
295
297
296 When two revision arguments are given, then changes are shown
298 When two revision arguments are given, then changes are shown
297 between those revisions. If only one revision is specified then
299 between those revisions. If only one revision is specified then
298 that revision is compared to the working directory, and, when no
300 that revision is compared to the working directory, and, when no
299 revisions are specified, the working directory files are compared
301 revisions are specified, the working directory files are compared
300 to its parent.'''
302 to its parent.'''
301 program = opts.get('program')
303 program = opts.get('program')
302 option = opts.get('option')
304 option = opts.get('option')
303 if not program:
305 if not program:
304 program = 'diff'
306 program = 'diff'
305 option = option or ['-Npru']
307 option = option or ['-Npru']
306 cmdline = ' '.join(map(util.shellquote, [program] + option))
308 cmdline = ' '.join(map(util.shellquote, [program] + option))
307 return dodiff(ui, repo, cmdline, pats, opts)
309 return dodiff(ui, repo, cmdline, pats, opts)
308
310
309 def uisetup(ui):
311 def uisetup(ui):
310 for cmd, path in ui.configitems('extdiff'):
312 for cmd, path in ui.configitems('extdiff'):
311 path = util.expandpath(path)
313 path = util.expandpath(path)
312 if cmd.startswith('cmd.'):
314 if cmd.startswith('cmd.'):
313 cmd = cmd[4:]
315 cmd = cmd[4:]
314 if not path:
316 if not path:
315 path = util.findexe(cmd)
317 path = util.findexe(cmd)
316 if path is None:
318 if path is None:
317 path = filemerge.findexternaltool(ui, cmd) or cmd
319 path = filemerge.findexternaltool(ui, cmd) or cmd
318 diffopts = ui.config('extdiff', 'opts.' + cmd, '')
320 diffopts = ui.config('extdiff', 'opts.' + cmd, '')
319 cmdline = util.shellquote(path)
321 cmdline = util.shellquote(path)
320 if diffopts:
322 if diffopts:
321 cmdline += ' ' + diffopts
323 cmdline += ' ' + diffopts
322 elif cmd.startswith('opts.'):
324 elif cmd.startswith('opts.'):
323 continue
325 continue
324 else:
326 else:
325 if path:
327 if path:
326 # case "cmd = path opts"
328 # case "cmd = path opts"
327 cmdline = path
329 cmdline = path
328 diffopts = len(shlex.split(cmdline)) > 1
330 diffopts = len(shlex.split(cmdline)) > 1
329 else:
331 else:
330 # case "cmd ="
332 # case "cmd ="
331 path = util.findexe(cmd)
333 path = util.findexe(cmd)
332 if path is None:
334 if path is None:
333 path = filemerge.findexternaltool(ui, cmd) or cmd
335 path = filemerge.findexternaltool(ui, cmd) or cmd
334 cmdline = util.shellquote(path)
336 cmdline = util.shellquote(path)
335 diffopts = False
337 diffopts = False
336 # look for diff arguments in [diff-tools] then [merge-tools]
338 # look for diff arguments in [diff-tools] then [merge-tools]
337 if not diffopts:
339 if not diffopts:
338 args = ui.config('diff-tools', cmd+'.diffargs') or \
340 args = ui.config('diff-tools', cmd+'.diffargs') or \
339 ui.config('merge-tools', cmd+'.diffargs')
341 ui.config('merge-tools', cmd+'.diffargs')
340 if args:
342 if args:
341 cmdline += ' ' + args
343 cmdline += ' ' + args
342 def save(cmdline):
344 def save(cmdline):
343 '''use closure to save diff command to use'''
345 '''use closure to save diff command to use'''
344 def mydiff(ui, repo, *pats, **opts):
346 def mydiff(ui, repo, *pats, **opts):
345 options = ' '.join(map(util.shellquote, opts['option']))
347 options = ' '.join(map(util.shellquote, opts['option']))
346 if options:
348 if options:
347 options = ' ' + options
349 options = ' ' + options
348 return dodiff(ui, repo, cmdline + options, pats, opts)
350 return dodiff(ui, repo, cmdline + options, pats, opts)
349 doc = _('''\
351 doc = _('''\
350 use %(path)s to diff repository (or selected files)
352 use %(path)s to diff repository (or selected files)
351
353
352 Show differences between revisions for the specified files, using
354 Show differences between revisions for the specified files, using
353 the %(path)s program.
355 the %(path)s program.
354
356
355 When two revision arguments are given, then changes are shown
357 When two revision arguments are given, then changes are shown
356 between those revisions. If only one revision is specified then
358 between those revisions. If only one revision is specified then
357 that revision is compared to the working directory, and, when no
359 that revision is compared to the working directory, and, when no
358 revisions are specified, the working directory files are compared
360 revisions are specified, the working directory files are compared
359 to its parent.\
361 to its parent.\
360 ''') % {'path': util.uirepr(path)}
362 ''') % {'path': util.uirepr(path)}
361
363
362 # We must translate the docstring right away since it is
364 # We must translate the docstring right away since it is
363 # used as a format string. The string will unfortunately
365 # used as a format string. The string will unfortunately
364 # be translated again in commands.helpcmd and this will
366 # be translated again in commands.helpcmd and this will
365 # fail when the docstring contains non-ASCII characters.
367 # fail when the docstring contains non-ASCII characters.
366 # Decoding the string to a Unicode string here (using the
368 # Decoding the string to a Unicode string here (using the
367 # right encoding) prevents that.
369 # right encoding) prevents that.
368 mydiff.__doc__ = doc.decode(encoding.encoding)
370 mydiff.__doc__ = doc.decode(encoding.encoding)
369 return mydiff
371 return mydiff
370 cmdtable[cmd] = (save(cmdline),
372 cmdtable[cmd] = (save(cmdline),
371 cmdtable['extdiff'][1][1:],
373 extdiffopts[:],
372 _('hg %s [OPTION]... [FILE]...') % cmd)
374 _('hg %s [OPTION]... [FILE]...') % cmd)
General Comments 0
You need to be logged in to leave comments. Login now