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