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