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