##// END OF EJS Templates
extdiff: use reST syntax for literal block
Martin Geisler -
r9207:38d29d3b default
parent child Browse files
Show More
@@ -1,226 +1,226 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, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
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 to
10 The `extdiff' Mercurial extension allows you to use external programs to
11 compare revisions, or revision with working directory. The external diff
11 compare revisions, or revision with working directory. The external diff
12 programs are called with a configurable set of options and two non-option
12 programs are called with a configurable set of options and two non-option
13 arguments: paths to directories containing snapshots of files to compare.
13 arguments: paths to directories containing snapshots of files to compare.
14
14
15 The `extdiff' extension also allows to configure new diff commands, so you do
15 The `extdiff' extension also allows to configure new diff commands, so you do
16 not need to type "hg extdiff -p kdiff3" always.
16 not need to type "hg extdiff -p kdiff3" always. ::
17
17
18 [extdiff]
18 [extdiff]
19 # add new command that runs GNU diff(1) in 'context diff' mode
19 # add new command that runs GNU diff(1) in 'context diff' mode
20 cdiff = gdiff -Nprc5
20 cdiff = gdiff -Nprc5
21 ## or the old way:
21 ## or the old way:
22 #cmd.cdiff = gdiff
22 #cmd.cdiff = gdiff
23 #opts.cdiff = -Nprc5
23 #opts.cdiff = -Nprc5
24
24
25 # add new command called vdiff, runs kdiff3
25 # add new command called vdiff, runs kdiff3
26 vdiff = kdiff3
26 vdiff = kdiff3
27
27
28 # add new command called meld, runs meld (no need to name twice)
28 # add new command called meld, runs meld (no need to name twice)
29 meld =
29 meld =
30
30
31 # add new command called vimdiff, runs gvimdiff with DirDiff plugin (see
31 # add new command called vimdiff, runs gvimdiff with DirDiff plugin (see
32 # http://www.vim.org/scripts/script.php?script_id=102) Non English user, be
32 # http://www.vim.org/scripts/script.php?script_id=102) Non English user, be
33 # sure to put "let g:DirDiffDynamicDiffText = 1" in your .vimrc
33 # sure to put "let g:DirDiffDynamicDiffText = 1" in your .vimrc
34 vimdiff = gvim -f '+next' '+execute "DirDiff" argv(0) argv(1)'
34 vimdiff = gvim -f '+next' '+execute "DirDiff" argv(0) argv(1)'
35
35
36 You can use -I/-X and list of file or directory names like normal "hg diff"
36 You can use -I/-X and list of file or directory names like normal "hg diff"
37 command. The `extdiff' extension makes snapshots of only needed files, so
37 command. The `extdiff' extension makes snapshots of only needed files, so
38 running the external diff program will actually be pretty fast (at least
38 running the external diff program will actually be pretty fast (at least
39 faster than having to compare the entire tree).
39 faster than having to compare the entire tree).
40 '''
40 '''
41
41
42 from mercurial.i18n import _
42 from mercurial.i18n import _
43 from mercurial.node import short
43 from mercurial.node import short
44 from mercurial import cmdutil, util, commands
44 from mercurial import cmdutil, util, commands
45 import os, shlex, shutil, tempfile
45 import os, shlex, shutil, tempfile
46
46
47 def snapshot(ui, repo, files, node, tmproot):
47 def snapshot(ui, repo, files, node, tmproot):
48 '''snapshot files as of some revision
48 '''snapshot files as of some revision
49 if not using snapshot, -I/-X does not work and recursive diff
49 if not using snapshot, -I/-X does not work and recursive diff
50 in tools like kdiff3 and meld displays too many files.'''
50 in tools like kdiff3 and meld displays too many files.'''
51 dirname = os.path.basename(repo.root)
51 dirname = os.path.basename(repo.root)
52 if dirname == "":
52 if dirname == "":
53 dirname = "root"
53 dirname = "root"
54 if node is not None:
54 if node is not None:
55 dirname = '%s.%s' % (dirname, short(node))
55 dirname = '%s.%s' % (dirname, short(node))
56 base = os.path.join(tmproot, dirname)
56 base = os.path.join(tmproot, dirname)
57 os.mkdir(base)
57 os.mkdir(base)
58 if node is not None:
58 if node is not None:
59 ui.note(_('making snapshot of %d files from rev %s\n') %
59 ui.note(_('making snapshot of %d files from rev %s\n') %
60 (len(files), short(node)))
60 (len(files), short(node)))
61 else:
61 else:
62 ui.note(_('making snapshot of %d files from working directory\n') %
62 ui.note(_('making snapshot of %d files from working directory\n') %
63 (len(files)))
63 (len(files)))
64 wopener = util.opener(base)
64 wopener = util.opener(base)
65 fns_and_mtime = []
65 fns_and_mtime = []
66 ctx = repo[node]
66 ctx = repo[node]
67 for fn in files:
67 for fn in files:
68 wfn = util.pconvert(fn)
68 wfn = util.pconvert(fn)
69 if not wfn in ctx:
69 if not wfn in ctx:
70 # skipping new file after a merge ?
70 # skipping new file after a merge ?
71 continue
71 continue
72 ui.note(' %s\n' % wfn)
72 ui.note(' %s\n' % wfn)
73 dest = os.path.join(base, wfn)
73 dest = os.path.join(base, wfn)
74 fctx = ctx[wfn]
74 fctx = ctx[wfn]
75 data = repo.wwritedata(wfn, fctx.data())
75 data = repo.wwritedata(wfn, fctx.data())
76 if 'l' in fctx.flags():
76 if 'l' in fctx.flags():
77 wopener.symlink(data, wfn)
77 wopener.symlink(data, wfn)
78 else:
78 else:
79 wopener(wfn, 'w').write(data)
79 wopener(wfn, 'w').write(data)
80 if 'x' in fctx.flags():
80 if 'x' in fctx.flags():
81 util.set_flags(dest, False, True)
81 util.set_flags(dest, False, True)
82 if node is None:
82 if node is None:
83 fns_and_mtime.append((dest, repo.wjoin(fn), os.path.getmtime(dest)))
83 fns_and_mtime.append((dest, repo.wjoin(fn), os.path.getmtime(dest)))
84 return dirname, fns_and_mtime
84 return dirname, fns_and_mtime
85
85
86 def dodiff(ui, repo, diffcmd, diffopts, pats, opts):
86 def dodiff(ui, repo, diffcmd, diffopts, pats, opts):
87 '''Do the actuall diff:
87 '''Do the actuall diff:
88
88
89 - copy to a temp structure if diffing 2 internal revisions
89 - copy to a temp structure if diffing 2 internal revisions
90 - copy to a temp structure if diffing working revision with
90 - copy to a temp structure if diffing working revision with
91 another one and more than 1 file is changed
91 another one and more than 1 file is changed
92 - just invoke the diff for a single file in the working dir
92 - just invoke the diff for a single file in the working dir
93 '''
93 '''
94
94
95 revs = opts.get('rev')
95 revs = opts.get('rev')
96 change = opts.get('change')
96 change = opts.get('change')
97
97
98 if revs and change:
98 if revs and change:
99 msg = _('cannot specify --rev and --change at the same time')
99 msg = _('cannot specify --rev and --change at the same time')
100 raise util.Abort(msg)
100 raise util.Abort(msg)
101 elif change:
101 elif change:
102 node2 = repo.lookup(change)
102 node2 = repo.lookup(change)
103 node1 = repo[node2].parents()[0].node()
103 node1 = repo[node2].parents()[0].node()
104 else:
104 else:
105 node1, node2 = cmdutil.revpair(repo, revs)
105 node1, node2 = cmdutil.revpair(repo, revs)
106
106
107 matcher = cmdutil.match(repo, pats, opts)
107 matcher = cmdutil.match(repo, pats, opts)
108 modified, added, removed = repo.status(node1, node2, matcher)[:3]
108 modified, added, removed = repo.status(node1, node2, matcher)[:3]
109 if not (modified or added or removed):
109 if not (modified or added or removed):
110 return 0
110 return 0
111
111
112 tmproot = tempfile.mkdtemp(prefix='extdiff.')
112 tmproot = tempfile.mkdtemp(prefix='extdiff.')
113 dir2root = ''
113 dir2root = ''
114 try:
114 try:
115 # Always make a copy of node1
115 # Always make a copy of node1
116 dir1 = snapshot(ui, repo, modified + removed, node1, tmproot)[0]
116 dir1 = snapshot(ui, repo, modified + removed, node1, tmproot)[0]
117 changes = len(modified) + len(removed) + len(added)
117 changes = len(modified) + len(removed) + len(added)
118
118
119 # If node2 in not the wc or there is >1 change, copy it
119 # If node2 in not the wc or there is >1 change, copy it
120 if node2 or changes > 1:
120 if node2 or changes > 1:
121 dir2, fns_and_mtime = snapshot(ui, repo, modified + added, node2, tmproot)
121 dir2, fns_and_mtime = snapshot(ui, repo, modified + added, node2, tmproot)
122 else:
122 else:
123 # This lets the diff tool open the changed file directly
123 # This lets the diff tool open the changed file directly
124 dir2 = ''
124 dir2 = ''
125 dir2root = repo.root
125 dir2root = repo.root
126 fns_and_mtime = []
126 fns_and_mtime = []
127
127
128 # If only one change, diff the files instead of the directories
128 # If only one change, diff the files instead of the directories
129 if changes == 1 :
129 if changes == 1 :
130 if len(modified):
130 if len(modified):
131 dir1 = os.path.join(dir1, util.localpath(modified[0]))
131 dir1 = os.path.join(dir1, util.localpath(modified[0]))
132 dir2 = os.path.join(dir2root, dir2, util.localpath(modified[0]))
132 dir2 = os.path.join(dir2root, dir2, util.localpath(modified[0]))
133 elif len(removed) :
133 elif len(removed) :
134 dir1 = os.path.join(dir1, util.localpath(removed[0]))
134 dir1 = os.path.join(dir1, util.localpath(removed[0]))
135 dir2 = os.devnull
135 dir2 = os.devnull
136 else:
136 else:
137 dir1 = os.devnull
137 dir1 = os.devnull
138 dir2 = os.path.join(dir2root, dir2, util.localpath(added[0]))
138 dir2 = os.path.join(dir2root, dir2, util.localpath(added[0]))
139
139
140 cmdline = ('%s %s %s %s' %
140 cmdline = ('%s %s %s %s' %
141 (util.shellquote(diffcmd), ' '.join(diffopts),
141 (util.shellquote(diffcmd), ' '.join(diffopts),
142 util.shellquote(dir1), util.shellquote(dir2)))
142 util.shellquote(dir1), util.shellquote(dir2)))
143 ui.debug(_('running %r in %s\n') % (cmdline, tmproot))
143 ui.debug(_('running %r in %s\n') % (cmdline, tmproot))
144 util.system(cmdline, cwd=tmproot)
144 util.system(cmdline, cwd=tmproot)
145
145
146 for copy_fn, working_fn, mtime in fns_and_mtime:
146 for copy_fn, working_fn, mtime in fns_and_mtime:
147 if os.path.getmtime(copy_fn) != mtime:
147 if os.path.getmtime(copy_fn) != mtime:
148 ui.debug(_('file changed while diffing. '
148 ui.debug(_('file changed while diffing. '
149 'Overwriting: %s (src: %s)\n') % (working_fn, copy_fn))
149 'Overwriting: %s (src: %s)\n') % (working_fn, copy_fn))
150 util.copyfile(copy_fn, working_fn)
150 util.copyfile(copy_fn, working_fn)
151
151
152 return 1
152 return 1
153 finally:
153 finally:
154 ui.note(_('cleaning up temp directory\n'))
154 ui.note(_('cleaning up temp directory\n'))
155 shutil.rmtree(tmproot)
155 shutil.rmtree(tmproot)
156
156
157 def extdiff(ui, repo, *pats, **opts):
157 def extdiff(ui, repo, *pats, **opts):
158 '''use external program to diff repository (or selected files)
158 '''use external program to diff repository (or selected files)
159
159
160 Show differences between revisions for the specified files, using an
160 Show differences between revisions for the specified files, using an
161 external program. The default program used is diff, with default options
161 external program. The default program used is diff, with default options
162 "-Npru".
162 "-Npru".
163
163
164 To select a different program, use the -p/--program option. The program
164 To select a different program, use the -p/--program option. The program
165 will be passed the names of two directories to compare. To pass additional
165 will be passed the names of two directories to compare. To pass additional
166 options to the program, use -o/--option. These will be passed before the
166 options to the program, use -o/--option. These will be passed before the
167 names of the directories to compare.
167 names of the directories to compare.
168
168
169 When two revision arguments are given, then changes are shown between
169 When two revision arguments are given, then changes are shown between
170 those revisions. If only one revision is specified then that revision is
170 those revisions. If only one revision is specified then that revision is
171 compared to the working directory, and, when no revisions are specified,
171 compared to the working directory, and, when no revisions are specified,
172 the working directory files are compared to its parent.
172 the working directory files are compared to its parent.
173 '''
173 '''
174 program = opts['program'] or 'diff'
174 program = opts['program'] or 'diff'
175 if opts['program']:
175 if opts['program']:
176 option = opts['option']
176 option = opts['option']
177 else:
177 else:
178 option = opts['option'] or ['-Npru']
178 option = opts['option'] or ['-Npru']
179 return dodiff(ui, repo, program, option, pats, opts)
179 return dodiff(ui, repo, program, option, pats, opts)
180
180
181 cmdtable = {
181 cmdtable = {
182 "extdiff":
182 "extdiff":
183 (extdiff,
183 (extdiff,
184 [('p', 'program', '', _('comparison program to run')),
184 [('p', 'program', '', _('comparison program to run')),
185 ('o', 'option', [], _('pass option to comparison program')),
185 ('o', 'option', [], _('pass option to comparison program')),
186 ('r', 'rev', [], _('revision')),
186 ('r', 'rev', [], _('revision')),
187 ('c', 'change', '', _('change made by revision')),
187 ('c', 'change', '', _('change made by revision')),
188 ] + commands.walkopts,
188 ] + commands.walkopts,
189 _('hg extdiff [OPT]... [FILE]...')),
189 _('hg extdiff [OPT]... [FILE]...')),
190 }
190 }
191
191
192 def uisetup(ui):
192 def uisetup(ui):
193 for cmd, path in ui.configitems('extdiff'):
193 for cmd, path in ui.configitems('extdiff'):
194 if cmd.startswith('cmd.'):
194 if cmd.startswith('cmd.'):
195 cmd = cmd[4:]
195 cmd = cmd[4:]
196 if not path: path = cmd
196 if not path: path = cmd
197 diffopts = ui.config('extdiff', 'opts.' + cmd, '')
197 diffopts = ui.config('extdiff', 'opts.' + cmd, '')
198 diffopts = diffopts and [diffopts] or []
198 diffopts = diffopts and [diffopts] or []
199 elif cmd.startswith('opts.'):
199 elif cmd.startswith('opts.'):
200 continue
200 continue
201 else:
201 else:
202 # command = path opts
202 # command = path opts
203 if path:
203 if path:
204 diffopts = shlex.split(path)
204 diffopts = shlex.split(path)
205 path = diffopts.pop(0)
205 path = diffopts.pop(0)
206 else:
206 else:
207 path, diffopts = cmd, []
207 path, diffopts = cmd, []
208 def save(cmd, path, diffopts):
208 def save(cmd, path, diffopts):
209 '''use closure to save diff command to use'''
209 '''use closure to save diff command to use'''
210 def mydiff(ui, repo, *pats, **opts):
210 def mydiff(ui, repo, *pats, **opts):
211 return dodiff(ui, repo, path, diffopts, pats, opts)
211 return dodiff(ui, repo, path, diffopts, pats, opts)
212 mydiff.__doc__ = _('''\
212 mydiff.__doc__ = _('''\
213 use %(path)s to diff repository (or selected files)
213 use %(path)s to diff repository (or selected files)
214
214
215 Show differences between revisions for the specified files, using the
215 Show differences between revisions for the specified files, using the
216 %(path)s program.
216 %(path)s program.
217
217
218 When two revision arguments are given, then changes are shown between
218 When two revision arguments are given, then changes are shown between
219 those revisions. If only one revision is specified then that revision is
219 those revisions. If only one revision is specified then that revision is
220 compared to the working directory, and, when no revisions are specified,
220 compared to the working directory, and, when no revisions are specified,
221 the working directory files are compared to its parent.\
221 the working directory files are compared to its parent.\
222 ''') % dict(path=util.uirepr(path))
222 ''') % dict(path=util.uirepr(path))
223 return mydiff
223 return mydiff
224 cmdtable[cmd] = (save(cmd, path, diffopts),
224 cmdtable[cmd] = (save(cmd, path, diffopts),
225 cmdtable['extdiff'][1][1:],
225 cmdtable['extdiff'][1][1:],
226 _('hg %s [OPTION]... [FILE]...') % cmd)
226 _('hg %s [OPTION]... [FILE]...') % cmd)
General Comments 0
You need to be logged in to leave comments. Login now