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