##// END OF EJS Templates
extdiff: quote user-supplied options passed to shell...
Michael Fyles -
r23138:72a89cf8 stable
parent child Browse files
Show More
@@ -1,328 +1,327 b''
1 # extdiff.py - external diff program support for mercurial
1 # extdiff.py - external diff program support for mercurial
2 #
2 #
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''command to allow external programs to compare revisions
8 '''command to allow external programs to compare revisions
9
9
10 The extdiff Mercurial extension allows you to use external programs
10 The extdiff Mercurial extension allows you to use external programs
11 to compare revisions, or revision with working directory. The external
11 to compare revisions, or revision with working directory. The external
12 diff programs are called with a configurable set of options and two
12 diff programs are called with a configurable set of options and two
13 non-option arguments: paths to directories containing snapshots of
13 non-option arguments: paths to directories containing snapshots of
14 files to compare.
14 files to compare.
15
15
16 The extdiff extension also allows you to configure new diff commands, so
16 The extdiff extension also allows you to configure new diff commands, so
17 you do not need to type :hg:`extdiff -p kdiff3` always. ::
17 you do not need to type :hg:`extdiff -p kdiff3` always. ::
18
18
19 [extdiff]
19 [extdiff]
20 # add new command that runs GNU diff(1) in 'context diff' mode
20 # add new command that runs GNU diff(1) in 'context diff' mode
21 cdiff = gdiff -Nprc5
21 cdiff = gdiff -Nprc5
22 ## or the old way:
22 ## or the old way:
23 #cmd.cdiff = gdiff
23 #cmd.cdiff = gdiff
24 #opts.cdiff = -Nprc5
24 #opts.cdiff = -Nprc5
25
25
26 # add new command called vdiff, runs kdiff3
26 # add new command called vdiff, runs kdiff3
27 vdiff = kdiff3
27 vdiff = kdiff3
28
28
29 # add new command called meld, runs meld (no need to name twice)
29 # add new command called meld, runs meld (no need to name twice)
30 meld =
30 meld =
31
31
32 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
32 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
33 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
33 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
34 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
34 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
35 # your .vimrc
35 # your .vimrc
36 vimdiff = gvim -f "+next" \\
36 vimdiff = gvim -f "+next" \\
37 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
37 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
38
38
39 Tool arguments can include variables that are expanded at runtime::
39 Tool arguments can include variables that are expanded at runtime::
40
40
41 $parent1, $plabel1 - filename, descriptive label of first parent
41 $parent1, $plabel1 - filename, descriptive label of first parent
42 $child, $clabel - filename, descriptive label of child revision
42 $child, $clabel - filename, descriptive label of child revision
43 $parent2, $plabel2 - filename, descriptive label of second parent
43 $parent2, $plabel2 - filename, descriptive label of second parent
44 $root - repository root
44 $root - repository root
45 $parent is an alias for $parent1.
45 $parent is an alias for $parent1.
46
46
47 The extdiff extension will look in your [diff-tools] and [merge-tools]
47 The extdiff extension will look in your [diff-tools] and [merge-tools]
48 sections for diff tool arguments, when none are specified in [extdiff].
48 sections for diff tool arguments, when none are specified in [extdiff].
49
49
50 ::
50 ::
51
51
52 [extdiff]
52 [extdiff]
53 kdiff3 =
53 kdiff3 =
54
54
55 [diff-tools]
55 [diff-tools]
56 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
56 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
57
57
58 You can use -I/-X and list of file or directory names like normal
58 You can use -I/-X and list of file or directory names like normal
59 :hg:`diff` command. The extdiff extension makes snapshots of only
59 :hg:`diff` command. The extdiff extension makes snapshots of only
60 needed files, so running the external diff program will actually be
60 needed files, so running the external diff program will actually be
61 pretty fast (at least faster than having to compare the entire tree).
61 pretty fast (at least faster than having to compare the entire tree).
62 '''
62 '''
63
63
64 from mercurial.i18n import _
64 from mercurial.i18n import _
65 from mercurial.node import short, nullid
65 from mercurial.node import short, nullid
66 from mercurial import cmdutil, scmutil, util, commands, encoding
66 from mercurial import cmdutil, scmutil, util, commands, encoding
67 import os, shlex, shutil, tempfile, re
67 import os, shlex, shutil, tempfile, re
68
68
69 cmdtable = {}
69 cmdtable = {}
70 command = cmdutil.command(cmdtable)
70 command = cmdutil.command(cmdtable)
71 testedwith = 'internal'
71 testedwith = 'internal'
72
72
73 def snapshot(ui, repo, files, node, tmproot):
73 def snapshot(ui, repo, files, node, tmproot):
74 '''snapshot files as of some revision
74 '''snapshot files as of some revision
75 if not using snapshot, -I/-X does not work and recursive diff
75 if not using snapshot, -I/-X does not work and recursive diff
76 in tools like kdiff3 and meld displays too many files.'''
76 in tools like kdiff3 and meld displays too many files.'''
77 dirname = os.path.basename(repo.root)
77 dirname = os.path.basename(repo.root)
78 if dirname == "":
78 if dirname == "":
79 dirname = "root"
79 dirname = "root"
80 if node is not None:
80 if node is not None:
81 dirname = '%s.%s' % (dirname, short(node))
81 dirname = '%s.%s' % (dirname, short(node))
82 base = os.path.join(tmproot, dirname)
82 base = os.path.join(tmproot, dirname)
83 os.mkdir(base)
83 os.mkdir(base)
84 if node is not None:
84 if node is not None:
85 ui.note(_('making snapshot of %d files from rev %s\n') %
85 ui.note(_('making snapshot of %d files from rev %s\n') %
86 (len(files), short(node)))
86 (len(files), short(node)))
87 else:
87 else:
88 ui.note(_('making snapshot of %d files from working directory\n') %
88 ui.note(_('making snapshot of %d files from working directory\n') %
89 (len(files)))
89 (len(files)))
90 wopener = scmutil.opener(base)
90 wopener = scmutil.opener(base)
91 fns_and_mtime = []
91 fns_and_mtime = []
92 ctx = repo[node]
92 ctx = repo[node]
93 for fn in files:
93 for fn in files:
94 wfn = util.pconvert(fn)
94 wfn = util.pconvert(fn)
95 if wfn not in ctx:
95 if wfn not in ctx:
96 # File doesn't exist; could be a bogus modify
96 # File doesn't exist; could be a bogus modify
97 continue
97 continue
98 ui.note(' %s\n' % wfn)
98 ui.note(' %s\n' % wfn)
99 dest = os.path.join(base, wfn)
99 dest = os.path.join(base, wfn)
100 fctx = ctx[wfn]
100 fctx = ctx[wfn]
101 data = repo.wwritedata(wfn, fctx.data())
101 data = repo.wwritedata(wfn, fctx.data())
102 if 'l' in fctx.flags():
102 if 'l' in fctx.flags():
103 wopener.symlink(data, wfn)
103 wopener.symlink(data, wfn)
104 else:
104 else:
105 wopener.write(wfn, data)
105 wopener.write(wfn, data)
106 if 'x' in fctx.flags():
106 if 'x' in fctx.flags():
107 util.setflags(dest, False, True)
107 util.setflags(dest, False, True)
108 if node is None:
108 if node is None:
109 fns_and_mtime.append((dest, repo.wjoin(fn),
109 fns_and_mtime.append((dest, repo.wjoin(fn),
110 os.lstat(dest).st_mtime))
110 os.lstat(dest).st_mtime))
111 return dirname, fns_and_mtime
111 return dirname, fns_and_mtime
112
112
113 def dodiff(ui, repo, diffcmd, diffopts, pats, opts):
113 def dodiff(ui, repo, diffcmd, diffopts, pats, opts):
114 '''Do the actual diff:
114 '''Do the actual diff:
115
115
116 - copy to a temp structure if diffing 2 internal revisions
116 - copy to a temp structure if diffing 2 internal revisions
117 - copy to a temp structure if diffing working revision with
117 - copy to a temp structure if diffing working revision with
118 another one and more than 1 file is changed
118 another one and more than 1 file is changed
119 - just invoke the diff for a single file in the working dir
119 - just invoke the diff for a single file in the working dir
120 '''
120 '''
121
121
122 revs = opts.get('rev')
122 revs = opts.get('rev')
123 change = opts.get('change')
123 change = opts.get('change')
124 args = ' '.join(diffopts)
124 args = ' '.join(map(util.shellquote, diffopts))
125 do3way = '$parent2' in args
125 do3way = '$parent2' in args
126
126
127 if revs and change:
127 if revs and change:
128 msg = _('cannot specify --rev and --change at the same time')
128 msg = _('cannot specify --rev and --change at the same time')
129 raise util.Abort(msg)
129 raise util.Abort(msg)
130 elif change:
130 elif change:
131 node2 = scmutil.revsingle(repo, change, None).node()
131 node2 = scmutil.revsingle(repo, change, None).node()
132 node1a, node1b = repo.changelog.parents(node2)
132 node1a, node1b = repo.changelog.parents(node2)
133 else:
133 else:
134 node1a, node2 = scmutil.revpair(repo, revs)
134 node1a, node2 = scmutil.revpair(repo, revs)
135 if not revs:
135 if not revs:
136 node1b = repo.dirstate.p2()
136 node1b = repo.dirstate.p2()
137 else:
137 else:
138 node1b = nullid
138 node1b = nullid
139
139
140 # Disable 3-way merge if there is only one parent
140 # Disable 3-way merge if there is only one parent
141 if do3way:
141 if do3way:
142 if node1b == nullid:
142 if node1b == nullid:
143 do3way = False
143 do3way = False
144
144
145 matcher = scmutil.match(repo[node2], pats, opts)
145 matcher = scmutil.match(repo[node2], pats, opts)
146 mod_a, add_a, rem_a = map(set, repo.status(node1a, node2, matcher)[:3])
146 mod_a, add_a, rem_a = map(set, repo.status(node1a, node2, matcher)[:3])
147 if do3way:
147 if do3way:
148 mod_b, add_b, rem_b = map(set, repo.status(node1b, node2, matcher)[:3])
148 mod_b, add_b, rem_b = map(set, repo.status(node1b, node2, matcher)[:3])
149 else:
149 else:
150 mod_b, add_b, rem_b = set(), set(), set()
150 mod_b, add_b, rem_b = set(), set(), set()
151 modadd = mod_a | add_a | mod_b | add_b
151 modadd = mod_a | add_a | mod_b | add_b
152 common = modadd | rem_a | rem_b
152 common = modadd | rem_a | rem_b
153 if not common:
153 if not common:
154 return 0
154 return 0
155
155
156 tmproot = tempfile.mkdtemp(prefix='extdiff.')
156 tmproot = tempfile.mkdtemp(prefix='extdiff.')
157 try:
157 try:
158 # Always make a copy of node1a (and node1b, if applicable)
158 # Always make a copy of node1a (and node1b, if applicable)
159 dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a)
159 dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a)
160 dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot)[0]
160 dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot)[0]
161 rev1a = '@%d' % repo[node1a].rev()
161 rev1a = '@%d' % repo[node1a].rev()
162 if do3way:
162 if do3way:
163 dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b)
163 dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b)
164 dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot)[0]
164 dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot)[0]
165 rev1b = '@%d' % repo[node1b].rev()
165 rev1b = '@%d' % repo[node1b].rev()
166 else:
166 else:
167 dir1b = None
167 dir1b = None
168 rev1b = ''
168 rev1b = ''
169
169
170 fns_and_mtime = []
170 fns_and_mtime = []
171
171
172 # If node2 in not the wc or there is >1 change, copy it
172 # If node2 in not the wc or there is >1 change, copy it
173 dir2root = ''
173 dir2root = ''
174 rev2 = ''
174 rev2 = ''
175 if node2:
175 if node2:
176 dir2 = snapshot(ui, repo, modadd, node2, tmproot)[0]
176 dir2 = snapshot(ui, repo, modadd, node2, tmproot)[0]
177 rev2 = '@%d' % repo[node2].rev()
177 rev2 = '@%d' % repo[node2].rev()
178 elif len(common) > 1:
178 elif len(common) > 1:
179 #we only actually need to get the files to copy back to
179 #we only actually need to get the files to copy back to
180 #the working dir in this case (because the other cases
180 #the working dir in this case (because the other cases
181 #are: diffing 2 revisions or single file -- in which case
181 #are: diffing 2 revisions or single file -- in which case
182 #the file is already directly passed to the diff tool).
182 #the file is already directly passed to the diff tool).
183 dir2, fns_and_mtime = snapshot(ui, repo, modadd, None, tmproot)
183 dir2, fns_and_mtime = snapshot(ui, repo, modadd, None, tmproot)
184 else:
184 else:
185 # This lets the diff tool open the changed file directly
185 # This lets the diff tool open the changed file directly
186 dir2 = ''
186 dir2 = ''
187 dir2root = repo.root
187 dir2root = repo.root
188
188
189 label1a = rev1a
189 label1a = rev1a
190 label1b = rev1b
190 label1b = rev1b
191 label2 = rev2
191 label2 = rev2
192
192
193 # If only one change, diff the files instead of the directories
193 # If only one change, diff the files instead of the directories
194 # Handle bogus modifies correctly by checking if the files exist
194 # Handle bogus modifies correctly by checking if the files exist
195 if len(common) == 1:
195 if len(common) == 1:
196 common_file = util.localpath(common.pop())
196 common_file = util.localpath(common.pop())
197 dir1a = os.path.join(tmproot, dir1a, common_file)
197 dir1a = os.path.join(tmproot, dir1a, common_file)
198 label1a = common_file + rev1a
198 label1a = common_file + rev1a
199 if not os.path.isfile(dir1a):
199 if not os.path.isfile(dir1a):
200 dir1a = os.devnull
200 dir1a = os.devnull
201 if do3way:
201 if do3way:
202 dir1b = os.path.join(tmproot, dir1b, common_file)
202 dir1b = os.path.join(tmproot, dir1b, common_file)
203 label1b = common_file + rev1b
203 label1b = common_file + rev1b
204 if not os.path.isfile(dir1b):
204 if not os.path.isfile(dir1b):
205 dir1b = os.devnull
205 dir1b = os.devnull
206 dir2 = os.path.join(dir2root, dir2, common_file)
206 dir2 = os.path.join(dir2root, dir2, common_file)
207 label2 = common_file + rev2
207 label2 = common_file + rev2
208
208
209 # Function to quote file/dir names in the argument string.
209 # Function to quote file/dir names in the argument string.
210 # When not operating in 3-way mode, an empty string is
210 # When not operating in 3-way mode, an empty string is
211 # returned for parent2
211 # returned for parent2
212 replace = {'parent': dir1a, 'parent1': dir1a, 'parent2': dir1b,
212 replace = {'parent': dir1a, 'parent1': dir1a, 'parent2': dir1b,
213 'plabel1': label1a, 'plabel2': label1b,
213 'plabel1': label1a, 'plabel2': label1b,
214 'clabel': label2, 'child': dir2,
214 'clabel': label2, 'child': dir2,
215 'root': repo.root}
215 'root': repo.root}
216 def quote(match):
216 def quote(match):
217 key = match.group()[1:]
217 key = match.group()[1:]
218 if not do3way and key == 'parent2':
218 if not do3way and key == 'parent2':
219 return ''
219 return ''
220 return util.shellquote(replace[key])
220 return util.shellquote(replace[key])
221
221
222 # Match parent2 first, so 'parent1?' will match both parent1 and parent
222 # Match parent2 first, so 'parent1?' will match both parent1 and parent
223 regex = '\$(parent2|parent1?|child|plabel1|plabel2|clabel|root)'
223 regex = '\$(parent2|parent1?|child|plabel1|plabel2|clabel|root)'
224 if not do3way and not re.search(regex, args):
224 if not do3way and not re.search(regex, args):
225 args += ' $parent1 $child'
225 args += ' $parent1 $child'
226 args = re.sub(regex, quote, args)
226 args = re.sub(regex, quote, args)
227 cmdline = util.shellquote(diffcmd) + ' ' + args
227 cmdline = util.shellquote(diffcmd) + ' ' + args
228
228
229 ui.debug('running %r in %s\n' % (cmdline, tmproot))
229 ui.debug('running %r in %s\n' % (cmdline, tmproot))
230 util.system(cmdline, cwd=tmproot, out=ui.fout)
230 util.system(cmdline, cwd=tmproot, out=ui.fout)
231
231
232 for copy_fn, working_fn, mtime in fns_and_mtime:
232 for copy_fn, working_fn, mtime in fns_and_mtime:
233 if os.lstat(copy_fn).st_mtime != mtime:
233 if os.lstat(copy_fn).st_mtime != mtime:
234 ui.debug('file changed while diffing. '
234 ui.debug('file changed while diffing. '
235 'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn))
235 'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn))
236 util.copyfile(copy_fn, working_fn)
236 util.copyfile(copy_fn, working_fn)
237
237
238 return 1
238 return 1
239 finally:
239 finally:
240 ui.note(_('cleaning up temp directory\n'))
240 ui.note(_('cleaning up temp directory\n'))
241 shutil.rmtree(tmproot)
241 shutil.rmtree(tmproot)
242
242
243 @command('extdiff',
243 @command('extdiff',
244 [('p', 'program', '',
244 [('p', 'program', '',
245 _('comparison program to run'), _('CMD')),
245 _('comparison program to run'), _('CMD')),
246 ('o', 'option', [],
246 ('o', 'option', [],
247 _('pass option to comparison program'), _('OPT')),
247 _('pass option to comparison program'), _('OPT')),
248 ('r', 'rev', [], _('revision'), _('REV')),
248 ('r', 'rev', [], _('revision'), _('REV')),
249 ('c', 'change', '', _('change made by revision'), _('REV')),
249 ('c', 'change', '', _('change made by revision'), _('REV')),
250 ] + commands.walkopts,
250 ] + commands.walkopts,
251 _('hg extdiff [OPT]... [FILE]...'),
251 _('hg extdiff [OPT]... [FILE]...'),
252 inferrepo=True)
252 inferrepo=True)
253 def extdiff(ui, repo, *pats, **opts):
253 def extdiff(ui, repo, *pats, **opts):
254 '''use external program to diff repository (or selected files)
254 '''use external program to diff repository (or selected files)
255
255
256 Show differences between revisions for the specified files, using
256 Show differences between revisions for the specified files, using
257 an external program. The default program used is diff, with
257 an external program. The default program used is diff, with
258 default options "-Npru".
258 default options "-Npru".
259
259
260 To select a different program, use the -p/--program option. The
260 To select a different program, use the -p/--program option. The
261 program will be passed the names of two directories to compare. To
261 program will be passed the names of two directories to compare. To
262 pass additional options to the program, use -o/--option. These
262 pass additional options to the program, use -o/--option. These
263 will be passed before the names of the directories to compare.
263 will be passed before the names of the directories to compare.
264
264
265 When two revision arguments are given, then changes are shown
265 When two revision arguments are given, then changes are shown
266 between those revisions. If only one revision is specified then
266 between those revisions. If only one revision is specified then
267 that revision is compared to the working directory, and, when no
267 that revision is compared to the working directory, and, when no
268 revisions are specified, the working directory files are compared
268 revisions are specified, the working directory files are compared
269 to its parent.'''
269 to its parent.'''
270 program = opts.get('program')
270 program = opts.get('program')
271 option = opts.get('option')
271 option = opts.get('option')
272 if not program:
272 if not program:
273 program = 'diff'
273 program = 'diff'
274 option = option or ['-Npru']
274 option = option or ['-Npru']
275 return dodiff(ui, repo, program, option, pats, opts)
275 return dodiff(ui, repo, program, option, pats, opts)
276
276
277 def uisetup(ui):
277 def uisetup(ui):
278 for cmd, path in ui.configitems('extdiff'):
278 for cmd, path in ui.configitems('extdiff'):
279 if cmd.startswith('cmd.'):
279 if cmd.startswith('cmd.'):
280 cmd = cmd[4:]
280 cmd = cmd[4:]
281 if not path:
281 if not path:
282 path = cmd
282 path = cmd
283 diffopts = ui.config('extdiff', 'opts.' + cmd, '')
283 diffopts = shlex.split(ui.config('extdiff', 'opts.' + cmd, ''))
284 diffopts = diffopts and [diffopts] or []
285 elif cmd.startswith('opts.'):
284 elif cmd.startswith('opts.'):
286 continue
285 continue
287 else:
286 else:
288 # command = path opts
287 # command = path opts
289 if path:
288 if path:
290 diffopts = shlex.split(path)
289 diffopts = shlex.split(path)
291 path = diffopts.pop(0)
290 path = diffopts.pop(0)
292 else:
291 else:
293 path, diffopts = cmd, []
292 path, diffopts = cmd, []
294 # look for diff arguments in [diff-tools] then [merge-tools]
293 # look for diff arguments in [diff-tools] then [merge-tools]
295 if diffopts == []:
294 if diffopts == []:
296 args = ui.config('diff-tools', cmd+'.diffargs') or \
295 args = ui.config('diff-tools', cmd+'.diffargs') or \
297 ui.config('merge-tools', cmd+'.diffargs')
296 ui.config('merge-tools', cmd+'.diffargs')
298 if args:
297 if args:
299 diffopts = shlex.split(args)
298 diffopts = shlex.split(args)
300 def save(cmd, path, diffopts):
299 def save(cmd, path, diffopts):
301 '''use closure to save diff command to use'''
300 '''use closure to save diff command to use'''
302 def mydiff(ui, repo, *pats, **opts):
301 def mydiff(ui, repo, *pats, **opts):
303 return dodiff(ui, repo, path, diffopts + opts['option'],
302 return dodiff(ui, repo, path, diffopts + opts['option'],
304 pats, opts)
303 pats, opts)
305 doc = _('''\
304 doc = _('''\
306 use %(path)s to diff repository (or selected files)
305 use %(path)s to diff repository (or selected files)
307
306
308 Show differences between revisions for the specified files, using
307 Show differences between revisions for the specified files, using
309 the %(path)s program.
308 the %(path)s program.
310
309
311 When two revision arguments are given, then changes are shown
310 When two revision arguments are given, then changes are shown
312 between those revisions. If only one revision is specified then
311 between those revisions. If only one revision is specified then
313 that revision is compared to the working directory, and, when no
312 that revision is compared to the working directory, and, when no
314 revisions are specified, the working directory files are compared
313 revisions are specified, the working directory files are compared
315 to its parent.\
314 to its parent.\
316 ''') % {'path': util.uirepr(path)}
315 ''') % {'path': util.uirepr(path)}
317
316
318 # We must translate the docstring right away since it is
317 # We must translate the docstring right away since it is
319 # used as a format string. The string will unfortunately
318 # used as a format string. The string will unfortunately
320 # be translated again in commands.helpcmd and this will
319 # be translated again in commands.helpcmd and this will
321 # fail when the docstring contains non-ASCII characters.
320 # fail when the docstring contains non-ASCII characters.
322 # Decoding the string to a Unicode string here (using the
321 # Decoding the string to a Unicode string here (using the
323 # right encoding) prevents that.
322 # right encoding) prevents that.
324 mydiff.__doc__ = doc.decode(encoding.encoding)
323 mydiff.__doc__ = doc.decode(encoding.encoding)
325 return mydiff
324 return mydiff
326 cmdtable[cmd] = (save(cmd, path, diffopts),
325 cmdtable[cmd] = (save(cmd, path, diffopts),
327 cmdtable['extdiff'][1][1:],
326 cmdtable['extdiff'][1][1:],
328 _('hg %s [OPTION]... [FILE]...') % cmd)
327 _('hg %s [OPTION]... [FILE]...') % cmd)
@@ -1,202 +1,214 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 $ echo "[extdiff]" >> $HGRCPATH
19 $ echo "[extdiff]" >> $HGRCPATH
20 $ echo "cmd.falabala=echo" >> $HGRCPATH
20 $ echo "cmd.falabala=echo" >> $HGRCPATH
21 $ echo "opts.falabala=diffing" >> $HGRCPATH
21 $ echo "opts.falabala=diffing" >> $HGRCPATH
22 $ echo "cmd.edspace=echo" >> $HGRCPATH
23 $ echo 'opts.edspace="name <user@example.com>"' >> $HGRCPATH
22
24
23 $ hg falabala
25 $ hg falabala
24 diffing a.000000000000 a
26 diffing a.000000000000 a
25 [1]
27 [1]
26
28
27 $ hg help falabala
29 $ hg help falabala
28 hg falabala [OPTION]... [FILE]...
30 hg falabala [OPTION]... [FILE]...
29
31
30 use 'echo' to diff repository (or selected files)
32 use 'echo' to diff repository (or selected files)
31
33
32 Show differences between revisions for the specified files, using the
34 Show differences between revisions for the specified files, using the
33 'echo' program.
35 'echo' program.
34
36
35 When two revision arguments are given, then changes are shown between
37 When two revision arguments are given, then changes are shown between
36 those revisions. If only one revision is specified then that revision is
38 those revisions. If only one revision is specified then that revision is
37 compared to the working directory, and, when no revisions are specified,
39 compared to the working directory, and, when no revisions are specified,
38 the working directory files are compared to its parent.
40 the working directory files are compared to its parent.
39
41
40 options ([+] can be repeated):
42 options ([+] can be repeated):
41
43
42 -o --option OPT [+] pass option to comparison program
44 -o --option OPT [+] pass option to comparison program
43 -r --rev REV [+] revision
45 -r --rev REV [+] revision
44 -c --change REV change made by revision
46 -c --change REV change made by revision
45 -I --include PATTERN [+] include names matching the given patterns
47 -I --include PATTERN [+] include names matching the given patterns
46 -X --exclude PATTERN [+] exclude names matching the given patterns
48 -X --exclude PATTERN [+] exclude names matching the given patterns
47
49
48 (some details hidden, use --verbose to show complete help)
50 (some details hidden, use --verbose to show complete help)
49
51
50 $ hg ci -d '0 0' -mtest1
52 $ hg ci -d '0 0' -mtest1
51
53
52 $ echo b >> a
54 $ echo b >> a
53 $ hg ci -d '1 0' -mtest2
55 $ hg ci -d '1 0' -mtest2
54
56
55 Should diff cloned files directly:
57 Should diff cloned files directly:
56
58
57 $ hg falabala -r 0:1
59 $ hg falabala -r 0:1
58 diffing */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
60 diffing */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
59 [1]
61 [1]
60
62
61 Test diff during merge:
63 Test diff during merge:
62
64
63 $ hg update -C 0
65 $ hg update -C 0
64 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
66 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
65 $ echo c >> c
67 $ echo c >> c
66 $ hg add c
68 $ hg add c
67 $ hg ci -m "new branch" -d '1 0'
69 $ hg ci -m "new branch" -d '1 0'
68 created new head
70 created new head
69 $ hg merge 1
71 $ hg merge 1
70 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
72 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
71 (branch merge, don't forget to commit)
73 (branch merge, don't forget to commit)
72
74
73 Should diff cloned file against wc file:
75 Should diff cloned file against wc file:
74
76
75 $ hg falabala
77 $ hg falabala
76 diffing */extdiff.*/a.2a13a4d2da36/a */a/a (glob)
78 diffing */extdiff.*/a.2a13a4d2da36/a */a/a (glob)
77 [1]
79 [1]
78
80
79
81
80 Test --change option:
82 Test --change option:
81
83
82 $ hg ci -d '2 0' -mtest3
84 $ hg ci -d '2 0' -mtest3
83 $ hg falabala -c 1
85 $ hg falabala -c 1
84 diffing */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
86 diffing */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
85 [1]
87 [1]
86
88
87 Check diff are made from the first parent:
89 Check diff are made from the first parent:
88
90
89 $ hg falabala -c 3 || echo "diff-like tools yield a non-zero exit code"
91 $ hg falabala -c 3 || echo "diff-like tools yield a non-zero exit code"
90 diffing */extdiff.*/a.2a13a4d2da36/a a.46c0e4daeb72/a (glob)
92 diffing */extdiff.*/a.2a13a4d2da36/a a.46c0e4daeb72/a (glob)
91 diff-like tools yield a non-zero exit code
93 diff-like tools yield a non-zero exit code
92
94
93 #if execbit
95 #if execbit
94
96
95 Test extdiff of multiple files in tmp dir:
97 Test extdiff of multiple files in tmp dir:
96
98
97 $ hg update -C 0 > /dev/null
99 $ hg update -C 0 > /dev/null
98 $ echo changed > a
100 $ echo changed > a
99 $ echo changed > b
101 $ echo changed > b
100 $ chmod +x b
102 $ chmod +x b
101
103
102 Diff in working directory, before:
104 Diff in working directory, before:
103
105
104 $ hg diff --git
106 $ hg diff --git
105 diff --git a/a b/a
107 diff --git a/a b/a
106 --- a/a
108 --- a/a
107 +++ b/a
109 +++ b/a
108 @@ -1,1 +1,1 @@
110 @@ -1,1 +1,1 @@
109 -a
111 -a
110 +changed
112 +changed
111 diff --git a/b b/b
113 diff --git a/b b/b
112 old mode 100644
114 old mode 100644
113 new mode 100755
115 new mode 100755
114 --- a/b
116 --- a/b
115 +++ b/b
117 +++ b/b
116 @@ -1,1 +1,1 @@
118 @@ -1,1 +1,1 @@
117 -b
119 -b
118 +changed
120 +changed
119
121
120
122
121 Edit with extdiff -p:
123 Edit with extdiff -p:
122
124
123 Prepare custom diff/edit tool:
125 Prepare custom diff/edit tool:
124
126
125 $ cat > 'diff tool.py' << EOT
127 $ cat > 'diff tool.py' << EOT
126 > #!/usr/bin/env python
128 > #!/usr/bin/env python
127 > import time
129 > import time
128 > time.sleep(1) # avoid unchanged-timestamp problems
130 > time.sleep(1) # avoid unchanged-timestamp problems
129 > file('a/a', 'ab').write('edited\n')
131 > file('a/a', 'ab').write('edited\n')
130 > file('a/b', 'ab').write('edited\n')
132 > file('a/b', 'ab').write('edited\n')
131 > EOT
133 > EOT
132
134
133 $ chmod +x 'diff tool.py'
135 $ chmod +x 'diff tool.py'
134
136
135 will change to /tmp/extdiff.TMP and populate directories a.TMP and a
137 will change to /tmp/extdiff.TMP and populate directories a.TMP and a
136 and start tool
138 and start tool
137
139
138 $ hg extdiff -p "`pwd`/diff tool.py"
140 $ hg extdiff -p "`pwd`/diff tool.py"
139 [1]
141 [1]
140
142
141 Diff in working directory, after:
143 Diff in working directory, after:
142
144
143 $ hg diff --git
145 $ hg diff --git
144 diff --git a/a b/a
146 diff --git a/a b/a
145 --- a/a
147 --- a/a
146 +++ b/a
148 +++ b/a
147 @@ -1,1 +1,2 @@
149 @@ -1,1 +1,2 @@
148 -a
150 -a
149 +changed
151 +changed
150 +edited
152 +edited
151 diff --git a/b b/b
153 diff --git a/b b/b
152 old mode 100644
154 old mode 100644
153 new mode 100755
155 new mode 100755
154 --- a/b
156 --- a/b
155 +++ b/b
157 +++ b/b
156 @@ -1,1 +1,2 @@
158 @@ -1,1 +1,2 @@
157 -b
159 -b
158 +changed
160 +changed
159 +edited
161 +edited
160
162
161 Test extdiff with --option:
163 Test extdiff with --option:
162
164
163 $ hg extdiff -p echo -o this -c 1
165 $ hg extdiff -p echo -o this -c 1
164 this */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
166 this */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
165 [1]
167 [1]
166
168
167 $ hg falabala -o this -c 1
169 $ hg falabala -o this -c 1
168 diffing this */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
170 diffing this */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
169 [1]
171 [1]
170
172
173 Test extdiff's handling of options with spaces in them:
174
175 $ hg edspace -c 1
176 name <user@example.com> */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
177 [1]
178
179 $ hg extdiff -p echo -o "name <user@example.com>" -c 1
180 name <user@example.com> */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
181 [1]
182
171 Test with revsets:
183 Test with revsets:
172
184
173 $ hg extdif -p echo -c "rev(1)"
185 $ hg extdif -p echo -c "rev(1)"
174 */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
186 */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
175 [1]
187 [1]
176
188
177 $ hg extdif -p echo -r "0::1"
189 $ hg extdif -p echo -r "0::1"
178 */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
190 */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
179 [1]
191 [1]
180
192
181 $ cd ..
193 $ cd ..
182
194
183 #endif
195 #endif
184
196
185 #if symlink
197 #if symlink
186
198
187 Test symlinks handling (issue1909)
199 Test symlinks handling (issue1909)
188
200
189 $ hg init testsymlinks
201 $ hg init testsymlinks
190 $ cd testsymlinks
202 $ cd testsymlinks
191 $ echo a > a
203 $ echo a > a
192 $ hg ci -Am adda
204 $ hg ci -Am adda
193 adding a
205 adding a
194 $ echo a >> a
206 $ echo a >> a
195 $ ln -s missing linka
207 $ ln -s missing linka
196 $ hg add linka
208 $ hg add linka
197 $ hg falabala -r 0 --traceback
209 $ hg falabala -r 0 --traceback
198 diffing testsymlinks.07f494440405 testsymlinks
210 diffing testsymlinks.07f494440405 testsymlinks
199 [1]
211 [1]
200 $ cd ..
212 $ cd ..
201
213
202 #endif
214 #endif
General Comments 0
You need to be logged in to leave comments. Login now