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