##// END OF EJS Templates
extdiff: escape path for docstring (issue5301)...
Matt Mackall -
r29630:67b180c0 stable
parent child Browse files
Show More
@@ -1,390 +1,384 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 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 __future__ import absolute_import
63 from __future__ import absolute_import
64
64
65 import os
65 import os
66 import re
66 import re
67 import shlex
67 import shlex
68 import shutil
68 import shutil
69 import tempfile
69 import tempfile
70 from mercurial.i18n import _
70 from mercurial.i18n import _
71 from mercurial.node import (
71 from mercurial.node import (
72 nullid,
72 nullid,
73 short,
73 short,
74 )
74 )
75 from mercurial import (
75 from mercurial import (
76 archival,
76 archival,
77 cmdutil,
77 cmdutil,
78 commands,
78 commands,
79 encoding,
80 error,
79 error,
81 filemerge,
80 filemerge,
82 scmutil,
81 scmutil,
83 util,
82 util,
84 )
83 )
85
84
86 cmdtable = {}
85 cmdtable = {}
87 command = cmdutil.command(cmdtable)
86 command = cmdutil.command(cmdtable)
88 # Note for extension authors: ONLY specify testedwith = 'internal' for
87 # Note for extension authors: ONLY specify testedwith = 'internal' for
89 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
88 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
90 # be specifying the version(s) of Mercurial they are tested with, or
89 # be specifying the version(s) of Mercurial they are tested with, or
91 # leave the attribute unspecified.
90 # leave the attribute unspecified.
92 testedwith = 'internal'
91 testedwith = 'internal'
93
92
94 def snapshot(ui, repo, files, node, tmproot, listsubrepos):
93 def snapshot(ui, repo, files, node, tmproot, listsubrepos):
95 '''snapshot files as of some revision
94 '''snapshot files as of some revision
96 if not using snapshot, -I/-X does not work and recursive diff
95 if not using snapshot, -I/-X does not work and recursive diff
97 in tools like kdiff3 and meld displays too many files.'''
96 in tools like kdiff3 and meld displays too many files.'''
98 dirname = os.path.basename(repo.root)
97 dirname = os.path.basename(repo.root)
99 if dirname == "":
98 if dirname == "":
100 dirname = "root"
99 dirname = "root"
101 if node is not None:
100 if node is not None:
102 dirname = '%s.%s' % (dirname, short(node))
101 dirname = '%s.%s' % (dirname, short(node))
103 base = os.path.join(tmproot, dirname)
102 base = os.path.join(tmproot, dirname)
104 os.mkdir(base)
103 os.mkdir(base)
105 fns_and_mtime = []
104 fns_and_mtime = []
106
105
107 if node is not None:
106 if node is not None:
108 ui.note(_('making snapshot of %d files from rev %s\n') %
107 ui.note(_('making snapshot of %d files from rev %s\n') %
109 (len(files), short(node)))
108 (len(files), short(node)))
110 else:
109 else:
111 ui.note(_('making snapshot of %d files from working directory\n') %
110 ui.note(_('making snapshot of %d files from working directory\n') %
112 (len(files)))
111 (len(files)))
113
112
114 if files:
113 if files:
115 repo.ui.setconfig("ui", "archivemeta", False)
114 repo.ui.setconfig("ui", "archivemeta", False)
116
115
117 archival.archive(repo, base, node, 'files',
116 archival.archive(repo, base, node, 'files',
118 matchfn=scmutil.matchfiles(repo, files),
117 matchfn=scmutil.matchfiles(repo, files),
119 subrepos=listsubrepos)
118 subrepos=listsubrepos)
120
119
121 for fn in sorted(files):
120 for fn in sorted(files):
122 wfn = util.pconvert(fn)
121 wfn = util.pconvert(fn)
123 ui.note(' %s\n' % wfn)
122 ui.note(' %s\n' % wfn)
124
123
125 if node is None:
124 if node is None:
126 dest = os.path.join(base, wfn)
125 dest = os.path.join(base, wfn)
127
126
128 fns_and_mtime.append((dest, repo.wjoin(fn),
127 fns_and_mtime.append((dest, repo.wjoin(fn),
129 os.lstat(dest).st_mtime))
128 os.lstat(dest).st_mtime))
130 return dirname, fns_and_mtime
129 return dirname, fns_and_mtime
131
130
132 def dodiff(ui, repo, cmdline, pats, opts):
131 def dodiff(ui, repo, cmdline, pats, opts):
133 '''Do the actual diff:
132 '''Do the actual diff:
134
133
135 - copy to a temp structure if diffing 2 internal revisions
134 - copy to a temp structure if diffing 2 internal revisions
136 - copy to a temp structure if diffing working revision with
135 - copy to a temp structure if diffing working revision with
137 another one and more than 1 file is changed
136 another one and more than 1 file is changed
138 - just invoke the diff for a single file in the working dir
137 - just invoke the diff for a single file in the working dir
139 '''
138 '''
140
139
141 revs = opts.get('rev')
140 revs = opts.get('rev')
142 change = opts.get('change')
141 change = opts.get('change')
143 do3way = '$parent2' in cmdline
142 do3way = '$parent2' in cmdline
144
143
145 if revs and change:
144 if revs and change:
146 msg = _('cannot specify --rev and --change at the same time')
145 msg = _('cannot specify --rev and --change at the same time')
147 raise error.Abort(msg)
146 raise error.Abort(msg)
148 elif change:
147 elif change:
149 node2 = scmutil.revsingle(repo, change, None).node()
148 node2 = scmutil.revsingle(repo, change, None).node()
150 node1a, node1b = repo.changelog.parents(node2)
149 node1a, node1b = repo.changelog.parents(node2)
151 else:
150 else:
152 node1a, node2 = scmutil.revpair(repo, revs)
151 node1a, node2 = scmutil.revpair(repo, revs)
153 if not revs:
152 if not revs:
154 node1b = repo.dirstate.p2()
153 node1b = repo.dirstate.p2()
155 else:
154 else:
156 node1b = nullid
155 node1b = nullid
157
156
158 # Disable 3-way merge if there is only one parent
157 # Disable 3-way merge if there is only one parent
159 if do3way:
158 if do3way:
160 if node1b == nullid:
159 if node1b == nullid:
161 do3way = False
160 do3way = False
162
161
163 subrepos=opts.get('subrepos')
162 subrepos=opts.get('subrepos')
164
163
165 matcher = scmutil.match(repo[node2], pats, opts)
164 matcher = scmutil.match(repo[node2], pats, opts)
166
165
167 if opts.get('patch'):
166 if opts.get('patch'):
168 if subrepos:
167 if subrepos:
169 raise error.Abort(_('--patch cannot be used with --subrepos'))
168 raise error.Abort(_('--patch cannot be used with --subrepos'))
170 if node2 is None:
169 if node2 is None:
171 raise error.Abort(_('--patch requires two revisions'))
170 raise error.Abort(_('--patch requires two revisions'))
172 else:
171 else:
173 mod_a, add_a, rem_a = map(set, repo.status(node1a, node2, matcher,
172 mod_a, add_a, rem_a = map(set, repo.status(node1a, node2, matcher,
174 listsubrepos=subrepos)[:3])
173 listsubrepos=subrepos)[:3])
175 if do3way:
174 if do3way:
176 mod_b, add_b, rem_b = map(set,
175 mod_b, add_b, rem_b = map(set,
177 repo.status(node1b, node2, matcher,
176 repo.status(node1b, node2, matcher,
178 listsubrepos=subrepos)[:3])
177 listsubrepos=subrepos)[:3])
179 else:
178 else:
180 mod_b, add_b, rem_b = set(), set(), set()
179 mod_b, add_b, rem_b = set(), set(), set()
181 modadd = mod_a | add_a | mod_b | add_b
180 modadd = mod_a | add_a | mod_b | add_b
182 common = modadd | rem_a | rem_b
181 common = modadd | rem_a | rem_b
183 if not common:
182 if not common:
184 return 0
183 return 0
185
184
186 tmproot = tempfile.mkdtemp(prefix='extdiff.')
185 tmproot = tempfile.mkdtemp(prefix='extdiff.')
187 try:
186 try:
188 if not opts.get('patch'):
187 if not opts.get('patch'):
189 # Always make a copy of node1a (and node1b, if applicable)
188 # Always make a copy of node1a (and node1b, if applicable)
190 dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a)
189 dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a)
191 dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot,
190 dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot,
192 subrepos)[0]
191 subrepos)[0]
193 rev1a = '@%d' % repo[node1a].rev()
192 rev1a = '@%d' % repo[node1a].rev()
194 if do3way:
193 if do3way:
195 dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b)
194 dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b)
196 dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot,
195 dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot,
197 subrepos)[0]
196 subrepos)[0]
198 rev1b = '@%d' % repo[node1b].rev()
197 rev1b = '@%d' % repo[node1b].rev()
199 else:
198 else:
200 dir1b = None
199 dir1b = None
201 rev1b = ''
200 rev1b = ''
202
201
203 fns_and_mtime = []
202 fns_and_mtime = []
204
203
205 # If node2 in not the wc or there is >1 change, copy it
204 # If node2 in not the wc or there is >1 change, copy it
206 dir2root = ''
205 dir2root = ''
207 rev2 = ''
206 rev2 = ''
208 if node2:
207 if node2:
209 dir2 = snapshot(ui, repo, modadd, node2, tmproot, subrepos)[0]
208 dir2 = snapshot(ui, repo, modadd, node2, tmproot, subrepos)[0]
210 rev2 = '@%d' % repo[node2].rev()
209 rev2 = '@%d' % repo[node2].rev()
211 elif len(common) > 1:
210 elif len(common) > 1:
212 #we only actually need to get the files to copy back to
211 #we only actually need to get the files to copy back to
213 #the working dir in this case (because the other cases
212 #the working dir in this case (because the other cases
214 #are: diffing 2 revisions or single file -- in which case
213 #are: diffing 2 revisions or single file -- in which case
215 #the file is already directly passed to the diff tool).
214 #the file is already directly passed to the diff tool).
216 dir2, fns_and_mtime = snapshot(ui, repo, modadd, None, tmproot,
215 dir2, fns_and_mtime = snapshot(ui, repo, modadd, None, tmproot,
217 subrepos)
216 subrepos)
218 else:
217 else:
219 # This lets the diff tool open the changed file directly
218 # This lets the diff tool open the changed file directly
220 dir2 = ''
219 dir2 = ''
221 dir2root = repo.root
220 dir2root = repo.root
222
221
223 label1a = rev1a
222 label1a = rev1a
224 label1b = rev1b
223 label1b = rev1b
225 label2 = rev2
224 label2 = rev2
226
225
227 # If only one change, diff the files instead of the directories
226 # If only one change, diff the files instead of the directories
228 # Handle bogus modifies correctly by checking if the files exist
227 # Handle bogus modifies correctly by checking if the files exist
229 if len(common) == 1:
228 if len(common) == 1:
230 common_file = util.localpath(common.pop())
229 common_file = util.localpath(common.pop())
231 dir1a = os.path.join(tmproot, dir1a, common_file)
230 dir1a = os.path.join(tmproot, dir1a, common_file)
232 label1a = common_file + rev1a
231 label1a = common_file + rev1a
233 if not os.path.isfile(dir1a):
232 if not os.path.isfile(dir1a):
234 dir1a = os.devnull
233 dir1a = os.devnull
235 if do3way:
234 if do3way:
236 dir1b = os.path.join(tmproot, dir1b, common_file)
235 dir1b = os.path.join(tmproot, dir1b, common_file)
237 label1b = common_file + rev1b
236 label1b = common_file + rev1b
238 if not os.path.isfile(dir1b):
237 if not os.path.isfile(dir1b):
239 dir1b = os.devnull
238 dir1b = os.devnull
240 dir2 = os.path.join(dir2root, dir2, common_file)
239 dir2 = os.path.join(dir2root, dir2, common_file)
241 label2 = common_file + rev2
240 label2 = common_file + rev2
242 else:
241 else:
243 template = 'hg-%h.patch'
242 template = 'hg-%h.patch'
244 cmdutil.export(repo, [repo[node1a].rev(), repo[node2].rev()],
243 cmdutil.export(repo, [repo[node1a].rev(), repo[node2].rev()],
245 template=repo.vfs.reljoin(tmproot, template),
244 template=repo.vfs.reljoin(tmproot, template),
246 match=matcher)
245 match=matcher)
247 label1a = cmdutil.makefilename(repo, template, node1a)
246 label1a = cmdutil.makefilename(repo, template, node1a)
248 label2 = cmdutil.makefilename(repo, template, node2)
247 label2 = cmdutil.makefilename(repo, template, node2)
249 dir1a = repo.vfs.reljoin(tmproot, label1a)
248 dir1a = repo.vfs.reljoin(tmproot, label1a)
250 dir2 = repo.vfs.reljoin(tmproot, label2)
249 dir2 = repo.vfs.reljoin(tmproot, label2)
251 dir1b = None
250 dir1b = None
252 label1b = None
251 label1b = None
253 fns_and_mtime = []
252 fns_and_mtime = []
254
253
255 # Function to quote file/dir names in the argument string.
254 # Function to quote file/dir names in the argument string.
256 # When not operating in 3-way mode, an empty string is
255 # When not operating in 3-way mode, an empty string is
257 # returned for parent2
256 # returned for parent2
258 replace = {'parent': dir1a, 'parent1': dir1a, 'parent2': dir1b,
257 replace = {'parent': dir1a, 'parent1': dir1a, 'parent2': dir1b,
259 'plabel1': label1a, 'plabel2': label1b,
258 'plabel1': label1a, 'plabel2': label1b,
260 'clabel': label2, 'child': dir2,
259 'clabel': label2, 'child': dir2,
261 'root': repo.root}
260 'root': repo.root}
262 def quote(match):
261 def quote(match):
263 pre = match.group(2)
262 pre = match.group(2)
264 key = match.group(3)
263 key = match.group(3)
265 if not do3way and key == 'parent2':
264 if not do3way and key == 'parent2':
266 return pre
265 return pre
267 return pre + util.shellquote(replace[key])
266 return pre + util.shellquote(replace[key])
268
267
269 # Match parent2 first, so 'parent1?' will match both parent1 and parent
268 # Match parent2 first, so 'parent1?' will match both parent1 and parent
270 regex = (r'''(['"]?)([^\s'"$]*)'''
269 regex = (r'''(['"]?)([^\s'"$]*)'''
271 r'\$(parent2|parent1?|child|plabel1|plabel2|clabel|root)\1')
270 r'\$(parent2|parent1?|child|plabel1|plabel2|clabel|root)\1')
272 if not do3way and not re.search(regex, cmdline):
271 if not do3way and not re.search(regex, cmdline):
273 cmdline += ' $parent1 $child'
272 cmdline += ' $parent1 $child'
274 cmdline = re.sub(regex, quote, cmdline)
273 cmdline = re.sub(regex, quote, cmdline)
275
274
276 ui.debug('running %r in %s\n' % (cmdline, tmproot))
275 ui.debug('running %r in %s\n' % (cmdline, tmproot))
277 ui.system(cmdline, cwd=tmproot)
276 ui.system(cmdline, cwd=tmproot)
278
277
279 for copy_fn, working_fn, mtime in fns_and_mtime:
278 for copy_fn, working_fn, mtime in fns_and_mtime:
280 if os.lstat(copy_fn).st_mtime != mtime:
279 if os.lstat(copy_fn).st_mtime != mtime:
281 ui.debug('file changed while diffing. '
280 ui.debug('file changed while diffing. '
282 'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn))
281 'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn))
283 util.copyfile(copy_fn, working_fn)
282 util.copyfile(copy_fn, working_fn)
284
283
285 return 1
284 return 1
286 finally:
285 finally:
287 ui.note(_('cleaning up temp directory\n'))
286 ui.note(_('cleaning up temp directory\n'))
288 shutil.rmtree(tmproot)
287 shutil.rmtree(tmproot)
289
288
290 extdiffopts = [
289 extdiffopts = [
291 ('o', 'option', [],
290 ('o', 'option', [],
292 _('pass option to comparison program'), _('OPT')),
291 _('pass option to comparison program'), _('OPT')),
293 ('r', 'rev', [], _('revision'), _('REV')),
292 ('r', 'rev', [], _('revision'), _('REV')),
294 ('c', 'change', '', _('change made by revision'), _('REV')),
293 ('c', 'change', '', _('change made by revision'), _('REV')),
295 ('', 'patch', None, _('compare patches for two revisions'))
294 ('', 'patch', None, _('compare patches for two revisions'))
296 ] + commands.walkopts + commands.subrepoopts
295 ] + commands.walkopts + commands.subrepoopts
297
296
298 @command('extdiff',
297 @command('extdiff',
299 [('p', 'program', '', _('comparison program to run'), _('CMD')),
298 [('p', 'program', '', _('comparison program to run'), _('CMD')),
300 ] + extdiffopts,
299 ] + extdiffopts,
301 _('hg extdiff [OPT]... [FILE]...'),
300 _('hg extdiff [OPT]... [FILE]...'),
302 inferrepo=True)
301 inferrepo=True)
303 def extdiff(ui, repo, *pats, **opts):
302 def extdiff(ui, repo, *pats, **opts):
304 '''use external program to diff repository (or selected files)
303 '''use external program to diff repository (or selected files)
305
304
306 Show differences between revisions for the specified files, using
305 Show differences between revisions for the specified files, using
307 an external program. The default program used is diff, with
306 an external program. The default program used is diff, with
308 default options "-Npru".
307 default options "-Npru".
309
308
310 To select a different program, use the -p/--program option. The
309 To select a different program, use the -p/--program option. The
311 program will be passed the names of two directories to compare. To
310 program will be passed the names of two directories to compare. To
312 pass additional options to the program, use -o/--option. These
311 pass additional options to the program, use -o/--option. These
313 will be passed before the names of the directories to compare.
312 will be passed before the names of the directories to compare.
314
313
315 When two revision arguments are given, then changes are shown
314 When two revision arguments are given, then changes are shown
316 between those revisions. If only one revision is specified then
315 between those revisions. If only one revision is specified then
317 that revision is compared to the working directory, and, when no
316 that revision is compared to the working directory, and, when no
318 revisions are specified, the working directory files are compared
317 revisions are specified, the working directory files are compared
319 to its parent.'''
318 to its parent.'''
320 program = opts.get('program')
319 program = opts.get('program')
321 option = opts.get('option')
320 option = opts.get('option')
322 if not program:
321 if not program:
323 program = 'diff'
322 program = 'diff'
324 option = option or ['-Npru']
323 option = option or ['-Npru']
325 cmdline = ' '.join(map(util.shellquote, [program] + option))
324 cmdline = ' '.join(map(util.shellquote, [program] + option))
326 return dodiff(ui, repo, cmdline, pats, opts)
325 return dodiff(ui, repo, cmdline, pats, opts)
327
326
328 def uisetup(ui):
327 def uisetup(ui):
329 for cmd, path in ui.configitems('extdiff'):
328 for cmd, path in ui.configitems('extdiff'):
330 path = util.expandpath(path)
329 path = util.expandpath(path)
331 if cmd.startswith('cmd.'):
330 if cmd.startswith('cmd.'):
332 cmd = cmd[4:]
331 cmd = cmd[4:]
333 if not path:
332 if not path:
334 path = util.findexe(cmd)
333 path = util.findexe(cmd)
335 if path is None:
334 if path is None:
336 path = filemerge.findexternaltool(ui, cmd) or cmd
335 path = filemerge.findexternaltool(ui, cmd) or cmd
337 diffopts = ui.config('extdiff', 'opts.' + cmd, '')
336 diffopts = ui.config('extdiff', 'opts.' + cmd, '')
338 cmdline = util.shellquote(path)
337 cmdline = util.shellquote(path)
339 if diffopts:
338 if diffopts:
340 cmdline += ' ' + diffopts
339 cmdline += ' ' + diffopts
341 elif cmd.startswith('opts.'):
340 elif cmd.startswith('opts.'):
342 continue
341 continue
343 else:
342 else:
344 if path:
343 if path:
345 # case "cmd = path opts"
344 # case "cmd = path opts"
346 cmdline = path
345 cmdline = path
347 diffopts = len(shlex.split(cmdline)) > 1
346 diffopts = len(shlex.split(cmdline)) > 1
348 else:
347 else:
349 # case "cmd ="
348 # case "cmd ="
350 path = util.findexe(cmd)
349 path = util.findexe(cmd)
351 if path is None:
350 if path is None:
352 path = filemerge.findexternaltool(ui, cmd) or cmd
351 path = filemerge.findexternaltool(ui, cmd) or cmd
353 cmdline = util.shellquote(path)
352 cmdline = util.shellquote(path)
354 diffopts = False
353 diffopts = False
355 # look for diff arguments in [diff-tools] then [merge-tools]
354 # look for diff arguments in [diff-tools] then [merge-tools]
356 if not diffopts:
355 if not diffopts:
357 args = ui.config('diff-tools', cmd+'.diffargs') or \
356 args = ui.config('diff-tools', cmd+'.diffargs') or \
358 ui.config('merge-tools', cmd+'.diffargs')
357 ui.config('merge-tools', cmd+'.diffargs')
359 if args:
358 if args:
360 cmdline += ' ' + args
359 cmdline += ' ' + args
361 def save(cmdline):
360 def save(cmdline):
362 '''use closure to save diff command to use'''
361 '''use closure to save diff command to use'''
363 def mydiff(ui, repo, *pats, **opts):
362 def mydiff(ui, repo, *pats, **opts):
364 options = ' '.join(map(util.shellquote, opts['option']))
363 options = ' '.join(map(util.shellquote, opts['option']))
365 if options:
364 if options:
366 options = ' ' + options
365 options = ' ' + options
367 return dodiff(ui, repo, cmdline + options, pats, opts)
366 return dodiff(ui, repo, cmdline + options, pats, opts)
368 doc = _('''\
367 # We can't pass non-ASCII through docstrings (and path is
368 # in an unknown encoding anyway)
369 docpath = path.encode("string-escape")
370 mydiff.__doc__ = '''\
369 use %(path)s to diff repository (or selected files)
371 use %(path)s to diff repository (or selected files)
370
372
371 Show differences between revisions for the specified files, using
373 Show differences between revisions for the specified files, using
372 the %(path)s program.
374 the %(path)s program.
373
375
374 When two revision arguments are given, then changes are shown
376 When two revision arguments are given, then changes are shown
375 between those revisions. If only one revision is specified then
377 between those revisions. If only one revision is specified then
376 that revision is compared to the working directory, and, when no
378 that revision is compared to the working directory, and, when no
377 revisions are specified, the working directory files are compared
379 revisions are specified, the working directory files are compared
378 to its parent.\
380 to its parent.\
379 ''') % {'path': util.uirepr(path)}
381 ''' % {'path': util.uirepr(docpath)}
380
381 # We must translate the docstring right away since it is
382 # used as a format string. The string will unfortunately
383 # be translated again in commands.helpcmd and this will
384 # fail when the docstring contains non-ASCII characters.
385 # Decoding the string to a Unicode string here (using the
386 # right encoding) prevents that.
387 mydiff.__doc__ = doc.decode(encoding.encoding)
388 return mydiff
382 return mydiff
389 command(cmd, extdiffopts[:], _('hg %s [OPTION]... [FILE]...') % cmd,
383 command(cmd, extdiffopts[:], _('hg %s [OPTION]... [FILE]...') % cmd,
390 inferrepo=True)(save(cmdline))
384 inferrepo=True)(save(cmdline))
@@ -1,391 +1,411 b''
1 $ echo "[extensions]" >> $HGRCPATH
1 $ echo "[extensions]" >> $HGRCPATH
2 $ echo "extdiff=" >> $HGRCPATH
2 $ echo "extdiff=" >> $HGRCPATH
3
3
4 $ hg init a
4 $ hg init a
5 $ cd a
5 $ cd a
6 $ echo a > a
6 $ echo a > a
7 $ echo b > b
7 $ echo b > b
8 $ hg add
8 $ hg add
9 adding a
9 adding a
10 adding b
10 adding b
11
11
12 Should diff cloned directories:
12 Should diff cloned directories:
13
13
14 $ hg extdiff -o -r $opt
14 $ hg extdiff -o -r $opt
15 Only in a: a
15 Only in a: a
16 Only in a: b
16 Only in a: b
17 [1]
17 [1]
18
18
19 $ cat <<EOF >> $HGRCPATH
19 $ cat <<EOF >> $HGRCPATH
20 > [extdiff]
20 > [extdiff]
21 > cmd.falabala = echo
21 > cmd.falabala = echo
22 > opts.falabala = diffing
22 > opts.falabala = diffing
23 > cmd.edspace = echo
23 > cmd.edspace = echo
24 > opts.edspace = "name <user@example.com>"
24 > opts.edspace = "name <user@example.com>"
25 > EOF
25 > EOF
26
26
27 $ hg falabala
27 $ hg falabala
28 diffing a.000000000000 a
28 diffing a.000000000000 a
29 [1]
29 [1]
30
30
31 $ hg help falabala
31 $ hg help falabala
32 hg falabala [OPTION]... [FILE]...
32 hg falabala [OPTION]... [FILE]...
33
33
34 use 'echo' to diff repository (or selected files)
34 use 'echo' to diff repository (or selected files)
35
35
36 Show differences between revisions for the specified files, using the
36 Show differences between revisions for the specified files, using the
37 'echo' program.
37 'echo' program.
38
38
39 When two revision arguments are given, then changes are shown between
39 When two revision arguments are given, then changes are shown between
40 those revisions. If only one revision is specified then that revision is
40 those revisions. If only one revision is specified then that revision is
41 compared to the working directory, and, when no revisions are specified,
41 compared to the working directory, and, when no revisions are specified,
42 the working directory files are compared to its parent.
42 the working directory files are compared to its parent.
43
43
44 options ([+] can be repeated):
44 options ([+] can be repeated):
45
45
46 -o --option OPT [+] pass option to comparison program
46 -o --option OPT [+] pass option to comparison program
47 -r --rev REV [+] revision
47 -r --rev REV [+] revision
48 -c --change REV change made by revision
48 -c --change REV change made by revision
49 --patch compare patches for two revisions
49 --patch compare patches for two revisions
50 -I --include PATTERN [+] include names matching the given patterns
50 -I --include PATTERN [+] include names matching the given patterns
51 -X --exclude PATTERN [+] exclude names matching the given patterns
51 -X --exclude PATTERN [+] exclude names matching the given patterns
52 -S --subrepos recurse into subrepositories
52 -S --subrepos recurse into subrepositories
53
53
54 (some details hidden, use --verbose to show complete help)
54 (some details hidden, use --verbose to show complete help)
55
55
56 $ hg ci -d '0 0' -mtest1
56 $ hg ci -d '0 0' -mtest1
57
57
58 $ echo b >> a
58 $ echo b >> a
59 $ hg ci -d '1 0' -mtest2
59 $ hg ci -d '1 0' -mtest2
60
60
61 Should diff cloned files directly:
61 Should diff cloned files directly:
62
62
63 #if windows
63 #if windows
64 $ hg falabala -r 0:1
64 $ hg falabala -r 0:1
65 diffing "*\\extdiff.*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob)
65 diffing "*\\extdiff.*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob)
66 [1]
66 [1]
67 #else
67 #else
68 $ hg falabala -r 0:1
68 $ hg falabala -r 0:1
69 diffing */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
69 diffing */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
70 [1]
70 [1]
71 #endif
71 #endif
72
72
73 Specifying an empty revision should abort.
73 Specifying an empty revision should abort.
74
74
75 $ hg extdiff -p diff --patch --rev 'ancestor()' --rev 1
75 $ hg extdiff -p diff --patch --rev 'ancestor()' --rev 1
76 abort: empty revision on one side of range
76 abort: empty revision on one side of range
77 [255]
77 [255]
78
78
79 Test diff during merge:
79 Test diff during merge:
80
80
81 $ hg update -C 0
81 $ hg update -C 0
82 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
82 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
83 $ echo c >> c
83 $ echo c >> c
84 $ hg add c
84 $ hg add c
85 $ hg ci -m "new branch" -d '1 0'
85 $ hg ci -m "new branch" -d '1 0'
86 created new head
86 created new head
87 $ hg merge 1
87 $ hg merge 1
88 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
88 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
89 (branch merge, don't forget to commit)
89 (branch merge, don't forget to commit)
90
90
91 Should diff cloned file against wc file:
91 Should diff cloned file against wc file:
92
92
93 #if windows
93 #if windows
94 $ hg falabala
94 $ hg falabala
95 diffing "*\\extdiff.*\\a.2a13a4d2da36\\a" "*\\a\\a" (glob)
95 diffing "*\\extdiff.*\\a.2a13a4d2da36\\a" "*\\a\\a" (glob)
96 [1]
96 [1]
97 #else
97 #else
98 $ hg falabala
98 $ hg falabala
99 diffing */extdiff.*/a.2a13a4d2da36/a */a/a (glob)
99 diffing */extdiff.*/a.2a13a4d2da36/a */a/a (glob)
100 [1]
100 [1]
101 #endif
101 #endif
102
102
103
103
104 Test --change option:
104 Test --change option:
105
105
106 $ hg ci -d '2 0' -mtest3
106 $ hg ci -d '2 0' -mtest3
107 #if windows
107 #if windows
108 $ hg falabala -c 1
108 $ hg falabala -c 1
109 diffing "*\\extdiff.*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob)
109 diffing "*\\extdiff.*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob)
110 [1]
110 [1]
111 #else
111 #else
112 $ hg falabala -c 1
112 $ hg falabala -c 1
113 diffing */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
113 diffing */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
114 [1]
114 [1]
115 #endif
115 #endif
116
116
117 Check diff are made from the first parent:
117 Check diff are made from the first parent:
118
118
119 #if windows
119 #if windows
120 $ hg falabala -c 3 || echo "diff-like tools yield a non-zero exit code"
120 $ hg falabala -c 3 || echo "diff-like tools yield a non-zero exit code"
121 diffing "*\\extdiff.*\\a.2a13a4d2da36\\a" "a.46c0e4daeb72\\a" (glob)
121 diffing "*\\extdiff.*\\a.2a13a4d2da36\\a" "a.46c0e4daeb72\\a" (glob)
122 diff-like tools yield a non-zero exit code
122 diff-like tools yield a non-zero exit code
123 #else
123 #else
124 $ hg falabala -c 3 || echo "diff-like tools yield a non-zero exit code"
124 $ hg falabala -c 3 || echo "diff-like tools yield a non-zero exit code"
125 diffing */extdiff.*/a.2a13a4d2da36/a a.46c0e4daeb72/a (glob)
125 diffing */extdiff.*/a.2a13a4d2da36/a a.46c0e4daeb72/a (glob)
126 diff-like tools yield a non-zero exit code
126 diff-like tools yield a non-zero exit code
127 #endif
127 #endif
128
128
129 issue3153: ensure using extdiff with removed subrepos doesn't crash:
129 issue3153: ensure using extdiff with removed subrepos doesn't crash:
130
130
131 $ hg init suba
131 $ hg init suba
132 $ cd suba
132 $ cd suba
133 $ echo suba > suba
133 $ echo suba > suba
134 $ hg add
134 $ hg add
135 adding suba
135 adding suba
136 $ hg ci -m "adding suba file"
136 $ hg ci -m "adding suba file"
137 $ cd ..
137 $ cd ..
138 $ echo suba=suba > .hgsub
138 $ echo suba=suba > .hgsub
139 $ hg add
139 $ hg add
140 adding .hgsub
140 adding .hgsub
141 $ hg ci -Sm "adding subrepo"
141 $ hg ci -Sm "adding subrepo"
142 $ echo > .hgsub
142 $ echo > .hgsub
143 $ hg ci -m "removing subrepo"
143 $ hg ci -m "removing subrepo"
144 $ hg falabala -r 4 -r 5 -S
144 $ hg falabala -r 4 -r 5 -S
145 diffing a.398e36faf9c6 a.5ab95fb166c4
145 diffing a.398e36faf9c6 a.5ab95fb166c4
146 [1]
146 [1]
147
147
148 issue4463: usage of command line configuration without additional quoting
148 issue4463: usage of command line configuration without additional quoting
149
149
150 $ cat <<EOF >> $HGRCPATH
150 $ cat <<EOF >> $HGRCPATH
151 > [extdiff]
151 > [extdiff]
152 > cmd.4463a = echo
152 > cmd.4463a = echo
153 > opts.4463a = a-naked 'single quoted' "double quoted"
153 > opts.4463a = a-naked 'single quoted' "double quoted"
154 > 4463b = echo b-naked 'single quoted' "double quoted"
154 > 4463b = echo b-naked 'single quoted' "double quoted"
155 > echo =
155 > echo =
156 > EOF
156 > EOF
157 $ hg update -q -C 0
157 $ hg update -q -C 0
158 $ echo a >> a
158 $ echo a >> a
159 #if windows
159 #if windows
160 $ hg --debug 4463a | grep '^running'
160 $ hg --debug 4463a | grep '^running'
161 running 'echo a-naked \'single quoted\' "double quoted" "*\\a" "*\\a"' in */extdiff.* (glob)
161 running 'echo a-naked \'single quoted\' "double quoted" "*\\a" "*\\a"' in */extdiff.* (glob)
162 $ hg --debug 4463b | grep '^running'
162 $ hg --debug 4463b | grep '^running'
163 running 'echo b-naked \'single quoted\' "double quoted" "*\\a" "*\\a"' in */extdiff.* (glob)
163 running 'echo b-naked \'single quoted\' "double quoted" "*\\a" "*\\a"' in */extdiff.* (glob)
164 $ hg --debug echo | grep '^running'
164 $ hg --debug echo | grep '^running'
165 running '*echo* "*\\a" "*\\a"' in */extdiff.* (glob)
165 running '*echo* "*\\a" "*\\a"' in */extdiff.* (glob)
166 #else
166 #else
167 $ hg --debug 4463a | grep '^running'
167 $ hg --debug 4463a | grep '^running'
168 running 'echo a-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob)
168 running 'echo a-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob)
169 $ hg --debug 4463b | grep '^running'
169 $ hg --debug 4463b | grep '^running'
170 running 'echo b-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob)
170 running 'echo b-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob)
171 $ hg --debug echo | grep '^running'
171 $ hg --debug echo | grep '^running'
172 running '*echo */a $TESTTMP/a/a' in */extdiff.* (glob)
172 running '*echo */a $TESTTMP/a/a' in */extdiff.* (glob)
173 #endif
173 #endif
174
174
175 (getting options from other than extdiff section)
175 (getting options from other than extdiff section)
176
176
177 $ cat <<EOF >> $HGRCPATH
177 $ cat <<EOF >> $HGRCPATH
178 > [extdiff]
178 > [extdiff]
179 > # using diff-tools diffargs
179 > # using diff-tools diffargs
180 > 4463b2 = echo
180 > 4463b2 = echo
181 > # using merge-tools diffargs
181 > # using merge-tools diffargs
182 > 4463b3 = echo
182 > 4463b3 = echo
183 > # no diffargs
183 > # no diffargs
184 > 4463b4 = echo
184 > 4463b4 = echo
185 > [diff-tools]
185 > [diff-tools]
186 > 4463b2.diffargs = b2-naked 'single quoted' "double quoted"
186 > 4463b2.diffargs = b2-naked 'single quoted' "double quoted"
187 > [merge-tools]
187 > [merge-tools]
188 > 4463b3.diffargs = b3-naked 'single quoted' "double quoted"
188 > 4463b3.diffargs = b3-naked 'single quoted' "double quoted"
189 > EOF
189 > EOF
190 #if windows
190 #if windows
191 $ hg --debug 4463b2 | grep '^running'
191 $ hg --debug 4463b2 | grep '^running'
192 running 'echo b2-naked \'single quoted\' "double quoted" "*\\a" "*\\a"' in */extdiff.* (glob)
192 running 'echo b2-naked \'single quoted\' "double quoted" "*\\a" "*\\a"' in */extdiff.* (glob)
193 $ hg --debug 4463b3 | grep '^running'
193 $ hg --debug 4463b3 | grep '^running'
194 running 'echo b3-naked \'single quoted\' "double quoted" "*\\a" "*\\a"' in */extdiff.* (glob)
194 running 'echo b3-naked \'single quoted\' "double quoted" "*\\a" "*\\a"' in */extdiff.* (glob)
195 $ hg --debug 4463b4 | grep '^running'
195 $ hg --debug 4463b4 | grep '^running'
196 running 'echo "*\\a" "*\\a"' in */extdiff.* (glob)
196 running 'echo "*\\a" "*\\a"' in */extdiff.* (glob)
197 $ hg --debug 4463b4 --option b4-naked --option 'being quoted' | grep '^running'
197 $ hg --debug 4463b4 --option b4-naked --option 'being quoted' | grep '^running'
198 running 'echo b4-naked "being quoted" "*\\a" "*\\a"' in */extdiff.* (glob)
198 running 'echo b4-naked "being quoted" "*\\a" "*\\a"' in */extdiff.* (glob)
199 $ hg --debug extdiff -p echo --option echo-naked --option 'being quoted' | grep '^running'
199 $ hg --debug extdiff -p echo --option echo-naked --option 'being quoted' | grep '^running'
200 running 'echo echo-naked "being quoted" "*\\a" "*\\a"' in */extdiff.* (glob)
200 running 'echo echo-naked "being quoted" "*\\a" "*\\a"' in */extdiff.* (glob)
201 #else
201 #else
202 $ hg --debug 4463b2 | grep '^running'
202 $ hg --debug 4463b2 | grep '^running'
203 running 'echo b2-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob)
203 running 'echo b2-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob)
204 $ hg --debug 4463b3 | grep '^running'
204 $ hg --debug 4463b3 | grep '^running'
205 running 'echo b3-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob)
205 running 'echo b3-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob)
206 $ hg --debug 4463b4 | grep '^running'
206 $ hg --debug 4463b4 | grep '^running'
207 running 'echo */a $TESTTMP/a/a' in */extdiff.* (glob)
207 running 'echo */a $TESTTMP/a/a' in */extdiff.* (glob)
208 $ hg --debug 4463b4 --option b4-naked --option 'being quoted' | grep '^running'
208 $ hg --debug 4463b4 --option b4-naked --option 'being quoted' | grep '^running'
209 running "echo b4-naked 'being quoted' */a $TESTTMP/a/a" in */extdiff.* (glob)
209 running "echo b4-naked 'being quoted' */a $TESTTMP/a/a" in */extdiff.* (glob)
210 $ hg --debug extdiff -p echo --option echo-naked --option 'being quoted' | grep '^running'
210 $ hg --debug extdiff -p echo --option echo-naked --option 'being quoted' | grep '^running'
211 running "echo echo-naked 'being quoted' */a $TESTTMP/a/a" in */extdiff.* (glob)
211 running "echo echo-naked 'being quoted' */a $TESTTMP/a/a" in */extdiff.* (glob)
212 #endif
212 #endif
213
213
214 $ touch 'sp ace'
214 $ touch 'sp ace'
215 $ hg add 'sp ace'
215 $ hg add 'sp ace'
216 $ hg ci -m 'sp ace'
216 $ hg ci -m 'sp ace'
217 created new head
217 created new head
218 $ echo > 'sp ace'
218 $ echo > 'sp ace'
219
219
220 Test pre-72a89cf86fcd backward compatibility with half-baked manual quoting
220 Test pre-72a89cf86fcd backward compatibility with half-baked manual quoting
221
221
222 $ cat <<EOF >> $HGRCPATH
222 $ cat <<EOF >> $HGRCPATH
223 > [extdiff]
223 > [extdiff]
224 > odd =
224 > odd =
225 > [merge-tools]
225 > [merge-tools]
226 > odd.diffargs = --foo='\$clabel' '\$clabel' "--bar=\$clabel" "\$clabel"
226 > odd.diffargs = --foo='\$clabel' '\$clabel' "--bar=\$clabel" "\$clabel"
227 > odd.executable = echo
227 > odd.executable = echo
228 > EOF
228 > EOF
229 #if windows
229 #if windows
230 TODO
230 TODO
231 #else
231 #else
232 $ hg --debug odd | grep '^running'
232 $ hg --debug odd | grep '^running'
233 running "*/echo --foo='sp ace' 'sp ace' --bar='sp ace' 'sp ace'" in * (glob)
233 running "*/echo --foo='sp ace' 'sp ace' --bar='sp ace' 'sp ace'" in * (glob)
234 #endif
234 #endif
235
235
236 Empty argument must be quoted
236 Empty argument must be quoted
237
237
238 $ cat <<EOF >> $HGRCPATH
238 $ cat <<EOF >> $HGRCPATH
239 > [extdiff]
239 > [extdiff]
240 > kdiff3 = echo
240 > kdiff3 = echo
241 > [merge-tools]
241 > [merge-tools]
242 > kdiff3.diffargs=--L1 \$plabel1 --L2 \$clabel \$parent \$child
242 > kdiff3.diffargs=--L1 \$plabel1 --L2 \$clabel \$parent \$child
243 > EOF
243 > EOF
244 #if windows
244 #if windows
245 $ hg --debug kdiff3 -r0 | grep '^running'
245 $ hg --debug kdiff3 -r0 | grep '^running'
246 running 'echo --L1 "@0" --L2 "" a.8a5febb7f867 a' in * (glob)
246 running 'echo --L1 "@0" --L2 "" a.8a5febb7f867 a' in * (glob)
247 #else
247 #else
248 $ hg --debug kdiff3 -r0 | grep '^running'
248 $ hg --debug kdiff3 -r0 | grep '^running'
249 running "echo --L1 '@0' --L2 '' a.8a5febb7f867 a" in * (glob)
249 running "echo --L1 '@0' --L2 '' a.8a5febb7f867 a" in * (glob)
250 #endif
250 #endif
251
251
252 #if execbit
252 #if execbit
253
253
254 Test extdiff of multiple files in tmp dir:
254 Test extdiff of multiple files in tmp dir:
255
255
256 $ hg update -C 0 > /dev/null
256 $ hg update -C 0 > /dev/null
257 $ echo changed > a
257 $ echo changed > a
258 $ echo changed > b
258 $ echo changed > b
259 $ chmod +x b
259 $ chmod +x b
260
260
261 Diff in working directory, before:
261 Diff in working directory, before:
262
262
263 $ hg diff --git
263 $ hg diff --git
264 diff --git a/a b/a
264 diff --git a/a b/a
265 --- a/a
265 --- a/a
266 +++ b/a
266 +++ b/a
267 @@ -1,1 +1,1 @@
267 @@ -1,1 +1,1 @@
268 -a
268 -a
269 +changed
269 +changed
270 diff --git a/b b/b
270 diff --git a/b b/b
271 old mode 100644
271 old mode 100644
272 new mode 100755
272 new mode 100755
273 --- a/b
273 --- a/b
274 +++ b/b
274 +++ b/b
275 @@ -1,1 +1,1 @@
275 @@ -1,1 +1,1 @@
276 -b
276 -b
277 +changed
277 +changed
278
278
279
279
280 Edit with extdiff -p:
280 Edit with extdiff -p:
281
281
282 Prepare custom diff/edit tool:
282 Prepare custom diff/edit tool:
283
283
284 $ cat > 'diff tool.py' << EOT
284 $ cat > 'diff tool.py' << EOT
285 > #!/usr/bin/env python
285 > #!/usr/bin/env python
286 > import time
286 > import time
287 > time.sleep(1) # avoid unchanged-timestamp problems
287 > time.sleep(1) # avoid unchanged-timestamp problems
288 > file('a/a', 'ab').write('edited\n')
288 > file('a/a', 'ab').write('edited\n')
289 > file('a/b', 'ab').write('edited\n')
289 > file('a/b', 'ab').write('edited\n')
290 > EOT
290 > EOT
291
291
292 $ chmod +x 'diff tool.py'
292 $ chmod +x 'diff tool.py'
293
293
294 will change to /tmp/extdiff.TMP and populate directories a.TMP and a
294 will change to /tmp/extdiff.TMP and populate directories a.TMP and a
295 and start tool
295 and start tool
296
296
297 $ hg extdiff -p "`pwd`/diff tool.py"
297 $ hg extdiff -p "`pwd`/diff tool.py"
298 [1]
298 [1]
299
299
300 Diff in working directory, after:
300 Diff in working directory, after:
301
301
302 $ hg diff --git
302 $ hg diff --git
303 diff --git a/a b/a
303 diff --git a/a b/a
304 --- a/a
304 --- a/a
305 +++ b/a
305 +++ b/a
306 @@ -1,1 +1,2 @@
306 @@ -1,1 +1,2 @@
307 -a
307 -a
308 +changed
308 +changed
309 +edited
309 +edited
310 diff --git a/b b/b
310 diff --git a/b b/b
311 old mode 100644
311 old mode 100644
312 new mode 100755
312 new mode 100755
313 --- a/b
313 --- a/b
314 +++ b/b
314 +++ b/b
315 @@ -1,1 +1,2 @@
315 @@ -1,1 +1,2 @@
316 -b
316 -b
317 +changed
317 +changed
318 +edited
318 +edited
319
319
320 Test extdiff with --option:
320 Test extdiff with --option:
321
321
322 $ hg extdiff -p echo -o this -c 1
322 $ hg extdiff -p echo -o this -c 1
323 this */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
323 this */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
324 [1]
324 [1]
325
325
326 $ hg falabala -o this -c 1
326 $ hg falabala -o this -c 1
327 diffing this */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
327 diffing this */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
328 [1]
328 [1]
329
329
330 Test extdiff's handling of options with spaces in them:
330 Test extdiff's handling of options with spaces in them:
331
331
332 $ hg edspace -c 1
332 $ hg edspace -c 1
333 name <user@example.com> */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
333 name <user@example.com> */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
334 [1]
334 [1]
335
335
336 $ hg extdiff -p echo -o "name <user@example.com>" -c 1
336 $ hg extdiff -p echo -o "name <user@example.com>" -c 1
337 name <user@example.com> */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
337 name <user@example.com> */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
338 [1]
338 [1]
339
339
340 Test with revsets:
340 Test with revsets:
341
341
342 $ hg extdif -p echo -c "rev(1)"
342 $ hg extdif -p echo -c "rev(1)"
343 */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
343 */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
344 [1]
344 [1]
345
345
346 $ hg extdif -p echo -r "0::1"
346 $ hg extdif -p echo -r "0::1"
347 */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
347 */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
348 [1]
348 [1]
349
349
350 Fallback to merge-tools.tool.executable|regkey
350 Fallback to merge-tools.tool.executable|regkey
351 $ mkdir dir
351 $ mkdir dir
352 $ cat > 'dir/tool.sh' << EOF
352 $ cat > 'dir/tool.sh' << EOF
353 > #!/bin/sh
353 > #!/bin/sh
354 > echo "** custom diff **"
354 > echo "** custom diff **"
355 > EOF
355 > EOF
356 $ chmod +x dir/tool.sh
356 $ chmod +x dir/tool.sh
357 $ tool=`pwd`/dir/tool.sh
357 $ tool=`pwd`/dir/tool.sh
358 $ hg --debug tl --config extdiff.tl= --config merge-tools.tl.executable=$tool
358 $ hg --debug tl --config extdiff.tl= --config merge-tools.tl.executable=$tool
359 making snapshot of 2 files from rev * (glob)
359 making snapshot of 2 files from rev * (glob)
360 a
360 a
361 b
361 b
362 making snapshot of 2 files from working directory
362 making snapshot of 2 files from working directory
363 a
363 a
364 b
364 b
365 running '$TESTTMP/a/dir/tool.sh a.* a' in */extdiff.* (glob)
365 running '$TESTTMP/a/dir/tool.sh a.* a' in */extdiff.* (glob)
366 ** custom diff **
366 ** custom diff **
367 cleaning up temp directory
367 cleaning up temp directory
368 [1]
368 [1]
369
369
370 $ cd ..
370 $ cd ..
371
371
372 #endif
372 #endif
373
373
374 #if symlink
374 #if symlink
375
375
376 Test symlinks handling (issue1909)
376 Test symlinks handling (issue1909)
377
377
378 $ hg init testsymlinks
378 $ hg init testsymlinks
379 $ cd testsymlinks
379 $ cd testsymlinks
380 $ echo a > a
380 $ echo a > a
381 $ hg ci -Am adda
381 $ hg ci -Am adda
382 adding a
382 adding a
383 $ echo a >> a
383 $ echo a >> a
384 $ ln -s missing linka
384 $ ln -s missing linka
385 $ hg add linka
385 $ hg add linka
386 $ hg falabala -r 0 --traceback
386 $ hg falabala -r 0 --traceback
387 diffing testsymlinks.07f494440405 testsymlinks
387 diffing testsymlinks.07f494440405 testsymlinks
388 [1]
388 [1]
389 $ cd ..
389 $ cd ..
390
390
391 #endif
391 #endif
392
393 Test handling of non-ASCII paths in generated docstrings (issue5301)
394
395 >>> open("u", "w").write("\xa5\xa5")
396 $ U=`cat u`
397
398 $ HGPLAIN=1 hg --config hgext.extdiff= --config extdiff.cmd.td=hi help -k xyzzy
399 abort: no matches
400 (try "hg help" for a list of topics)
401 [255]
402
403 $ HGPLAIN=1 hg --config hgext.extdiff= --config extdiff.cmd.td=hi help td > /dev/null
404
405 $ LC_MESSAGES=ja_JP.UTF-8 hg --config hgext.extdiff= --config extdiff.cmd.td=$U help -k xyzzy
406 abort: no matches
407 (try "hg help" for a list of topics)
408 [255]
409
410 $ LC_MESSAGES=ja_JP.UTF-8 hg --config hgext.extdiff= --config extdiff.cmd.td=$U help td | grep "^use"
411 use '\xa5\xa5' to diff repository (or selected files)
General Comments 0
You need to be logged in to leave comments. Login now