di 0.1.8 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +12 -9
- data/Rakefile +1 -1
- data/bin/di +6 -818
- data/di.gemspec +5 -4
- data/lib/di.rb +822 -0
- metadata +12 -4
data/README.rdoc
CHANGED
@@ -37,20 +37,23 @@ Run di --help for help.
|
|
37
37
|
|
38
38
|
== USAGE:
|
39
39
|
|
40
|
-
Just use di(1) for diff(1).
|
40
|
+
Just use di(1) for diff(1) as a wrapper.
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
file
|
42
|
+
There are a few occations you shouldn't simply substitute di(1) for
|
43
|
+
diff(1). For example, svn calls the external diff for each changed
|
44
|
+
file, so the pagination enabled by default would be annoying.
|
45
45
|
|
46
|
-
|
46
|
+
In such a case, you can have a non-paginating version of di as below:
|
47
47
|
|
48
|
-
|
48
|
+
# Give it another name
|
49
|
+
ln di ndi
|
49
50
|
|
51
|
+
# Set default options for the new command; put these lines in your
|
52
|
+
# shell's startup file
|
50
53
|
NDI_OPTIONS=--no-pager
|
51
54
|
export NDI_OPTIONS
|
52
55
|
|
53
|
-
Then
|
56
|
+
Then set diff-cmd to the new command in ~/.subversion/config.
|
54
57
|
|
55
58
|
[helpers]
|
56
59
|
diff-cmd = ndi
|
@@ -63,9 +66,9 @@ Then finally set diff-cmd to the command in ~/.subversion/config.
|
|
63
66
|
|
64
67
|
== INSTALL:
|
65
68
|
|
66
|
-
gem install di
|
69
|
+
gem install di
|
67
70
|
|
68
|
-
Or just
|
71
|
+
Or just install lib/di.rb as "di" to a directory in your PATH.
|
69
72
|
|
70
73
|
== COPYRIGHT:
|
71
74
|
|
data/Rakefile
CHANGED
data/bin/di
CHANGED
@@ -1,822 +1,10 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
# -*- mode: ruby; coding: utf-8 -*-
|
3
|
-
#
|
4
|
-
# di - a wrapper around GNU diff(1)
|
5
|
-
#
|
6
|
-
# Copyright (c) 2008, 2009, 2010 Akinori MUSHA
|
7
|
-
#
|
8
|
-
# All rights reserved.
|
9
|
-
#
|
10
|
-
# Redistribution and use in source and binary forms, with or without
|
11
|
-
# modification, are permitted provided that the following conditions
|
12
|
-
# are met:
|
13
|
-
# 1. Redistributions of source code must retain the above copyright
|
14
|
-
# notice, this list of conditions and the following disclaimer.
|
15
|
-
# 2. Redistributions in binary form must reproduce the above copyright
|
16
|
-
# notice, this list of conditions and the following disclaimer in the
|
17
|
-
# documentation and/or other materials provided with the distribution.
|
18
|
-
#
|
19
|
-
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
20
|
-
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
21
|
-
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
22
|
-
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
23
|
-
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
24
|
-
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
25
|
-
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
26
|
-
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
27
|
-
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
28
|
-
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
29
|
-
# SUCH DAMAGE.
|
30
2
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
ENV_NAME = "#{MYNAME.tr('-a-z', '_A-Z')}_OPTIONS"
|
37
|
-
EMPTYFILE = '/dev/null'
|
38
|
-
|
39
|
-
RSYNC_EXCLUDE_GLOBS = %w(
|
40
|
-
RCS SCCS CVS CVS.adm
|
41
|
-
RCSLOG cvslog.* tags TAGS
|
42
|
-
.make.state .nse_depinfo *~
|
43
|
-
\#* .\#* ,* _$*
|
44
|
-
*$ *.old *.bak *.BAK
|
45
|
-
*.orig *.rej *.del-* *.a
|
46
|
-
*.olb *.o *.obj *.so
|
47
|
-
*.exe *.Z *.elc *.ln
|
48
|
-
core .svn .git .bzr .hg
|
49
|
-
)
|
50
|
-
|
51
|
-
FIGNORE_GLOBS = ENV.fetch('FIGNORE', '').split(':').map { |pat|
|
52
|
-
'*' + pat
|
53
|
-
}
|
54
|
-
|
55
|
-
def main(args)
|
56
|
-
setup
|
57
|
-
|
58
|
-
parse_args!(args)
|
59
|
-
|
60
|
-
diff_main
|
61
|
-
|
62
|
-
exit $status
|
63
|
-
end
|
64
|
-
|
65
|
-
def warn(*lines)
|
66
|
-
lines.each { |line|
|
67
|
-
STDERR.puts "#{MYNAME}: #{line}"
|
68
|
-
}
|
69
|
-
end
|
70
|
-
|
71
|
-
def setup
|
72
|
-
require 'ostruct'
|
73
|
-
$diff = OpenStruct.new
|
74
|
-
$diff.exclude = []
|
75
|
-
$diff.include = []
|
76
|
-
$diff.flags = []
|
77
|
-
$diff.format_flags = []
|
78
|
-
$diff.format = :normal
|
79
|
-
$diff.custom_format_p = false
|
80
|
-
$diff.use_pager = false
|
81
|
-
$diff.colorize = false
|
82
|
-
$diff.highlight_whitespace = true
|
83
|
-
$diff.colors = {
|
84
|
-
:comment => "\033[1m",
|
85
|
-
:file1 => "\033[1m",
|
86
|
-
:file2 => "\033[1m",
|
87
|
-
:header => "\033[36m",
|
88
|
-
:function => "\033[m",
|
89
|
-
:new => "\033[32m",
|
90
|
-
:old => "\033[31m",
|
91
|
-
:changed => "\033[33m",
|
92
|
-
:unchanged => "",
|
93
|
-
:whitespace => "\033[41m",
|
94
|
-
:off => "\033[m",
|
95
|
-
}
|
96
|
-
end
|
97
|
-
|
98
|
-
def parse_args!(args)
|
99
|
-
require 'optparse'
|
100
|
-
|
101
|
-
banner = <<-"EOF"
|
102
|
-
#{MYNAME} - a wrapper around GNU diff(1)
|
103
|
-
version #{MYVERSION}
|
104
|
-
|
105
|
-
usage: #{MYNAME} [flags] [files]
|
106
|
-
EOF
|
107
|
-
|
108
|
-
opts = OptionParser.new(banner) { |opts|
|
109
|
-
miniTrueClass = Class.new
|
110
|
-
hash = OptionParser::CompletingHash.new
|
111
|
-
hash['-'] = false
|
112
|
-
opts.accept(miniTrueClass, hash) {|arg, val| val == nil or val}
|
113
|
-
|
114
|
-
opts.on('--[no-]pager',
|
115
|
-
'Pipe output into pager if stdout is a terminal. [!][*]') { |val|
|
116
|
-
$diff.use_pager = val if $stdout.tty?
|
117
|
-
}
|
118
|
-
opts.on('--[no-]color',
|
119
|
-
'Colorize output if stdout is a terminal and the format is unified or context. [!][*]') { |val|
|
120
|
-
$diff.colorize = val if $stdout.tty?
|
121
|
-
}
|
122
|
-
opts.on('--[no-]rsync-exclude', '--[no-]cvs-exclude',
|
123
|
-
'Exclude some kinds of files and directories a la rsync(1). [!][*]') { |val|
|
124
|
-
$diff.rsync_exclude = val
|
125
|
-
}
|
126
|
-
opts.on('--[no-]ignore-cvs-lines',
|
127
|
-
'Ignore CVS keyword lines. [!][*]') { |val|
|
128
|
-
$diff.ignore_cvs_lines = val
|
129
|
-
}
|
130
|
-
opts.on('--[no-]fignore-exclude',
|
131
|
-
'Ignore files having suffixes specified in FIGNORE. [!][*]') { |val|
|
132
|
-
$diff.fignore_exclude = val
|
133
|
-
}
|
134
|
-
opts.on('-R', '--relative[=-]', miniTrueClass,
|
135
|
-
'Use relative path names. [*]') { |val|
|
136
|
-
$diff.relative = val
|
137
|
-
}
|
138
|
-
opts.on('-i', '--ignore-case[=-]', miniTrueClass,
|
139
|
-
'Ignore case differences in file contents.') { |val|
|
140
|
-
set_flag('-i', val)
|
141
|
-
}
|
142
|
-
# not supported (yet)
|
143
|
-
#opts.on("--[no-]ignore-file-name-case",
|
144
|
-
# "Ignore case when comparing file names.") { |val|
|
145
|
-
# set_flag("--ignore-file-name-case", val)
|
146
|
-
#}
|
147
|
-
opts.on('-E', '--ignore-tab-expansion[=-]', miniTrueClass,
|
148
|
-
'Ignore changes due to tab expansion.') { |val|
|
149
|
-
set_flag('-E', val)
|
150
|
-
}
|
151
|
-
opts.on('-b', '--ignore-space-change[=-]', miniTrueClass,
|
152
|
-
'Ignore changes in the amount of white space.') { |val|
|
153
|
-
set_flag('-b', val)
|
154
|
-
}
|
155
|
-
opts.on('-w', '--ignore-all-space[=-]', miniTrueClass,
|
156
|
-
'Ignore all white space.') { |val|
|
157
|
-
set_flag('-w', val)
|
158
|
-
}
|
159
|
-
opts.on('-B', '--ignore-blank-lines[=-]', miniTrueClass,
|
160
|
-
'Ignore changes whose lines are all blank.') { |val|
|
161
|
-
set_flag('-B', val)
|
162
|
-
}
|
163
|
-
opts.on('-I RE', '--ignore-matching-lines=RE',
|
164
|
-
'Ignore changes whose lines all match RE.') { |val|
|
165
|
-
set_flag('-I', val)
|
166
|
-
}
|
167
|
-
opts.on('--[no-]strip-trailing-cr',
|
168
|
-
'Strip trailing carriage return on input.') { |val|
|
169
|
-
set_flag('--strip-trailing-cr', val)
|
170
|
-
}
|
171
|
-
opts.on('-a', '--text[=-]', miniTrueClass,
|
172
|
-
'Treat all files as text.') { |val|
|
173
|
-
set_flag('-a', val)
|
174
|
-
}
|
175
|
-
opts.on('-c[NUM]', '--context[=NUM]', Integer,
|
176
|
-
'Output NUM (default 3) lines of copied context.') { |val|
|
177
|
-
set_format_flag('-C', val ? val.to_s : '3')
|
178
|
-
}
|
179
|
-
opts.on('-C NUM', Integer,
|
180
|
-
'Output NUM lines of copied context.') { |val|
|
181
|
-
set_format_flag('-C', val.to_s)
|
182
|
-
}
|
183
|
-
opts.on('-u[NUM]', '--unified[=NUM]', Integer,
|
184
|
-
'Output NUM (default 3) lines of unified context. [!]') { |val|
|
185
|
-
set_format_flag('-U', val ? val.to_s : '3')
|
186
|
-
}
|
187
|
-
opts.on('-U NUM', Integer,
|
188
|
-
'Output NUM lines of unified context.') { |val|
|
189
|
-
set_format_flag('-U', val.to_s)
|
190
|
-
}
|
191
|
-
opts.on('-L LABEL', '--label=LABEL',
|
192
|
-
'Use LABEL instead of file name.') { |val|
|
193
|
-
set_flag('-L', val)
|
194
|
-
}
|
195
|
-
opts.on('-p', '--show-c-function[=-]', miniTrueClass,
|
196
|
-
'Show which C function each change is in. [!]') { |val|
|
197
|
-
set_flag('-p', val)
|
198
|
-
}
|
199
|
-
opts.on('-F RE', '--show-function-line=RE',
|
200
|
-
'Show the most recent line matching RE.') { |val|
|
201
|
-
set_flag('-F', val)
|
202
|
-
}
|
203
|
-
opts.on('-q', '--brief[=-]', miniTrueClass,
|
204
|
-
'Output only whether files differ.') { |val|
|
205
|
-
set_flag('-q', val)
|
206
|
-
}
|
207
|
-
opts.on('-e', '--ed[=-]', miniTrueClass,
|
208
|
-
'Output an ed script.') { |val|
|
209
|
-
if val
|
210
|
-
set_format_flag('-e', val)
|
211
|
-
end
|
212
|
-
}
|
213
|
-
opts.on('--normal[=-]', miniTrueClass,
|
214
|
-
'Output a normal diff.') { |val|
|
215
|
-
if val
|
216
|
-
set_format_flag('--normal', val)
|
217
|
-
end
|
218
|
-
}
|
219
|
-
opts.on('-n', '--rcs[=-]', miniTrueClass,
|
220
|
-
'Output an RCS format diff.') { |val|
|
221
|
-
if val
|
222
|
-
set_format_flag('-n', val)
|
223
|
-
end
|
224
|
-
}
|
225
|
-
opts.on('-y', '--side-by-side[=-]', miniTrueClass,
|
226
|
-
'Output in two columns.') { |val|
|
227
|
-
if val
|
228
|
-
set_format_flag('-y', val)
|
229
|
-
end
|
230
|
-
}
|
231
|
-
opts.on('-W NUM', '--width=NUM', Integer,
|
232
|
-
'Output at most NUM (default 130) print columns.') { |val|
|
233
|
-
set_flag('-W', val.to_s)
|
234
|
-
}
|
235
|
-
opts.on('--left-column[=-]', miniTrueClass,
|
236
|
-
'Output only the left column of common lines.') { |val|
|
237
|
-
set_flag('--left-column', val)
|
238
|
-
}
|
239
|
-
opts.on('--suppress-common-lines[=-]', miniTrueClass,
|
240
|
-
'Do not output common lines.') { |val|
|
241
|
-
set_flag('--suppress-common-lines', val)
|
242
|
-
}
|
243
|
-
opts.on('-D NAME', '--ifdef=NAME',
|
244
|
-
'Output merged file to show `#ifdef NAME\' diffs.') { |val|
|
245
|
-
set_format_flag('-D', val)
|
246
|
-
}
|
247
|
-
opts.on('--old-group-format=GFMT',
|
248
|
-
'Format old input groups with GFMT.') { |val|
|
249
|
-
set_custom_format_flag('--old-group-format', val)
|
250
|
-
}
|
251
|
-
opts.on('--new-group-format=GFMT',
|
252
|
-
'Format new input groups with GFMT.') { |val|
|
253
|
-
set_custom_format_flag('--new-group-format', val)
|
254
|
-
}
|
255
|
-
opts.on('--changed-group-format=GFMT',
|
256
|
-
'Format changed input groups with GFMT.') { |val|
|
257
|
-
set_custom_format_flag('--changed-group-format', val)
|
258
|
-
}
|
259
|
-
opts.on('--unchanged-group-format=GFMT',
|
260
|
-
'Format unchanged input groups with GFMT.') { |val|
|
261
|
-
set_custom_format_flag('--unchanged-group-format', val)
|
262
|
-
}
|
263
|
-
opts.on('--line-format=LFMT',
|
264
|
-
'Format all input lines with LFMT.') { |val|
|
265
|
-
set_custom_format_flag('--line-format', val)
|
266
|
-
}
|
267
|
-
opts.on('--old-line-format=LFMT',
|
268
|
-
'Format old input lines with LFMT.') { |val|
|
269
|
-
set_custom_format_flag('--old-line-format', val)
|
270
|
-
}
|
271
|
-
opts.on('--new-line-format=LFMT',
|
272
|
-
'Format new input lines with LFMT.') { |val|
|
273
|
-
set_custom_format_flag('--new-line-format', val)
|
274
|
-
}
|
275
|
-
opts.on('--unchanged-line-format=LFMT',
|
276
|
-
'Format unchanged input lines with LFMT.') { |val|
|
277
|
-
set_custom_format_flag('--unchanged-line-format', val)
|
278
|
-
}
|
279
|
-
opts.on('-l', '--paginate[=-]', miniTrueClass,
|
280
|
-
'Pass the output through `pr\' to paginate it.') { |val|
|
281
|
-
set_flag('-l', val)
|
282
|
-
}
|
283
|
-
opts.on('-t', '--expand-tabs[=-]', miniTrueClass,
|
284
|
-
'Expand tabs to spaces in output.') { |val|
|
285
|
-
set_flag('-t', val)
|
286
|
-
}
|
287
|
-
opts.on('-T', '--initial-tab[=-]', miniTrueClass,
|
288
|
-
'Make tabs line up by prepending a tab.') { |val|
|
289
|
-
set_flag('-T', '--initial-tab', val)
|
290
|
-
}
|
291
|
-
opts.on('--tabsize=NUM', Integer,
|
292
|
-
'Tab stops are every NUM (default 8) print columns.') { |val|
|
293
|
-
set_flag('--tabsize', val.to_s)
|
294
|
-
}
|
295
|
-
opts.on('-r', '--recursive[=-]', miniTrueClass,
|
296
|
-
'Recursively compare any subdirectories found. [!]') { |val|
|
297
|
-
set_flag('-r', val)
|
298
|
-
$diff.recursive = val
|
299
|
-
}
|
300
|
-
opts.on('-N', '--[no-]new-file[=-]', miniTrueClass,
|
301
|
-
'Treat absent files as empty. [!]') { |val|
|
302
|
-
set_flag('-N', val)
|
303
|
-
$diff.new_file = val
|
304
|
-
}
|
305
|
-
opts.on('--unidirectional-new-file[=-]', miniTrueClass,
|
306
|
-
'Treat absent first files as empty.') { |val|
|
307
|
-
set_flag('--unidirectional-new-file', val)
|
308
|
-
}
|
309
|
-
opts.on('-s', '--report-identical-files[=-]', miniTrueClass,
|
310
|
-
'Report when two files are the same.') { |val|
|
311
|
-
set_flag('-s', val)
|
312
|
-
}
|
313
|
-
opts.on('-x PAT', '--exclude=PAT',
|
314
|
-
'Exclude files that match PAT.') { |val|
|
315
|
-
$diff.exclude << val
|
316
|
-
}
|
317
|
-
opts.on('-X FILE', '--exclude-from=FILE',
|
318
|
-
'Exclude files that match any pattern in FILE.') { |val|
|
319
|
-
if val == '-'
|
320
|
-
$diff.exclude.concat(STDIN.read.split(/\n/))
|
321
|
-
else
|
322
|
-
$diff.exclude.concat(File.read(val).split(/\n/))
|
323
|
-
end
|
324
|
-
}
|
325
|
-
opts.on('--include=PAT',
|
326
|
-
'Do not exclude files that match PAT.') { |val|
|
327
|
-
$diff.include << val
|
328
|
-
}
|
329
|
-
opts.on('-S FILE', '--starting-file=FILE',
|
330
|
-
'Start with FILE when comparing directories.') { |val|
|
331
|
-
set_flag('-S', val)
|
332
|
-
}
|
333
|
-
opts.on('--from-file=FILE1',
|
334
|
-
'Compare FILE1 to all operands. FILE1 can be a directory.') { |val|
|
335
|
-
$diff.from_files = [val]
|
336
|
-
}
|
337
|
-
opts.on('--to-file=FILE2',
|
338
|
-
'Compare all operands to FILE2. FILE2 can be a directory.') { |val|
|
339
|
-
$diff.to_files = [val]
|
340
|
-
}
|
341
|
-
opts.on('--horizon-lines=NUM', Integer,
|
342
|
-
'Keep NUM lines of the common prefix and suffix.') { |val|
|
343
|
-
set_flag('--horizon-lines', val.to_s)
|
344
|
-
}
|
345
|
-
opts.on('-d', '--minimal[=-]', miniTrueClass,
|
346
|
-
'Try hard to find a smaller set of changes. [!]') { |val|
|
347
|
-
set_flag('-d', val)
|
348
|
-
}
|
349
|
-
opts.on('--speed-large-files[=-]', miniTrueClass,
|
350
|
-
'Assume large files and many scattered small changes.') { |val|
|
351
|
-
set_flag('--speed-large-files', val)
|
352
|
-
}
|
353
|
-
opts.on('-v', '--version',
|
354
|
-
'Output version info.') { |val|
|
355
|
-
print <<-"EOF"
|
356
|
-
#{MYNAME} version #{MYVERSION}
|
357
|
-
#{MYCOPYRIGHT}
|
358
|
-
|
359
|
-
----
|
360
|
-
EOF
|
361
|
-
system(DIFF_CMD, '--version')
|
362
|
-
exit
|
363
|
-
}
|
364
|
-
opts.on('--help',
|
365
|
-
'Output this help.') { |val|
|
366
|
-
invoke_pager
|
367
|
-
print opts, <<EOS
|
368
|
-
Options without the [*] sign will be passed through to diff(1).
|
369
|
-
Options marked as [!] sign are turned on by default. To turn them off,
|
370
|
-
specify -?- for short options and --no-??? for long options, respectively.
|
371
|
-
|
372
|
-
Environment variables:
|
373
|
-
EOS
|
374
|
-
[
|
375
|
-
['DIFF', 'Path to diff(1)'],
|
376
|
-
[ENV_NAME, 'User\'s preferred default options'],
|
377
|
-
['PAGER', 'Path to pager (more(1) is used if not defined)'],
|
378
|
-
].each { |name, description|
|
379
|
-
printf " %-14s %s\n", name, description
|
380
|
-
}
|
381
|
-
exit 0
|
382
|
-
}
|
383
|
-
}
|
384
|
-
|
385
|
-
begin
|
386
|
-
opts.parse('--rsync-exclude', '--fignore-exclude', '--ignore-cvs-lines',
|
387
|
-
'--pager', '--color',
|
388
|
-
'-U3', '-N', '-r', '-p', '-d')
|
389
|
-
|
390
|
-
if value = ENV[ENV_NAME]
|
391
|
-
require 'shellwords'
|
392
|
-
opts.parse(*value.shellsplit)
|
393
|
-
end
|
394
|
-
|
395
|
-
opts.parse!(args)
|
396
|
-
|
397
|
-
$diff.format_flags.each { |format_flag|
|
398
|
-
set_flag(*format_flag)
|
399
|
-
}
|
400
|
-
|
401
|
-
if $diff.ignore_cvs_lines
|
402
|
-
opts.parse('--ignore-matching-lines="^[^\x1b]*\$[A-Z][A-Za-z0-9][A-Za-z0-9]*\(:.*\)\{0,1\}\$')
|
403
|
-
end
|
404
|
-
rescue OptionParser::ParseError => e
|
405
|
-
warn e, "Try `#{MYNAME} --help' for more information."
|
406
|
-
exit 64
|
407
|
-
rescue => e
|
408
|
-
warn e
|
409
|
-
exit 1
|
410
|
-
end
|
411
|
-
|
412
|
-
begin
|
413
|
-
if $diff.from_files
|
414
|
-
$diff.to_files ||= args.dup
|
415
|
-
|
416
|
-
if $diff.to_files.empty?
|
417
|
-
raise "missing operand"
|
418
|
-
end
|
419
|
-
elsif $diff.to_files
|
420
|
-
$diff.from_files = args.dup
|
421
|
-
|
422
|
-
if $diff.from_files.empty?
|
423
|
-
raise "missing operand"
|
424
|
-
end
|
425
|
-
else
|
426
|
-
if args.size < 2
|
427
|
-
raise "missing operand"
|
428
|
-
end
|
429
|
-
|
430
|
-
if File.directory?(args[0])
|
431
|
-
$diff.to_files = args.dup
|
432
|
-
$diff.from_files = [$diff.to_files.shift]
|
433
|
-
else
|
434
|
-
$diff.from_files = args.dup
|
435
|
-
$diff.to_files = [$diff.from_files.pop]
|
436
|
-
end
|
437
|
-
end
|
438
|
-
|
439
|
-
if $diff.from_files.size != 1 && $diff.to_files.size != 1
|
440
|
-
raise "wrong number of files given"
|
441
|
-
end
|
442
|
-
rescue => e
|
443
|
-
warn e, "Try `#{MYNAME} --help' for more information."
|
444
|
-
exit 64
|
445
|
-
end
|
446
|
-
end
|
447
|
-
|
448
|
-
def invoke_pager
|
449
|
-
invoke_pager! if $diff.use_pager
|
450
|
-
end
|
451
|
-
|
452
|
-
def invoke_pager!
|
453
|
-
$stdout.flush
|
454
|
-
$stderr.flush
|
455
|
-
pr, pw = IO.pipe
|
456
|
-
ppid = Process.pid
|
457
|
-
pid = fork {
|
458
|
-
$stdin.reopen(pr)
|
459
|
-
pr.close
|
460
|
-
pw.close
|
461
|
-
IO.select([$stdin], nil, [$stdin])
|
462
|
-
if system(ENV['PAGER'] || 'more')
|
463
|
-
Process.kill(:TERM, ppid)
|
464
|
-
else
|
465
|
-
$stderr.puts "Pager failed."
|
466
|
-
Process.kill(:INT, ppid)
|
467
|
-
end
|
468
|
-
}
|
469
|
-
trap(:TERM) { exit(0) }
|
470
|
-
trap(:INT) { exit(130) }
|
471
|
-
$stdout.reopen(pw)
|
472
|
-
$stderr.reopen(pw) if $stderr.tty?
|
473
|
-
pw.close
|
474
|
-
at_exit {
|
475
|
-
$stdout.flush
|
476
|
-
$stderr.flush
|
477
|
-
$stdout.close
|
478
|
-
$stderr.close
|
479
|
-
Process.waitpid(pid)
|
480
|
-
}
|
481
|
-
end
|
482
|
-
|
483
|
-
def set_flag(flag, val)
|
484
|
-
case val
|
485
|
-
when false
|
486
|
-
$diff.flags.reject! { |f,| f == flag }
|
487
|
-
when true
|
488
|
-
$diff.flags.reject! { |f,| f == flag }
|
489
|
-
$diff.flags << [flag]
|
490
|
-
else
|
491
|
-
$diff.flags << [flag, val]
|
492
|
-
end
|
493
|
-
end
|
494
|
-
|
495
|
-
def set_format_flag(flag, *val)
|
496
|
-
$diff.format_flags.clear
|
497
|
-
$diff.custom_format_p = false
|
498
|
-
case flag
|
499
|
-
when '-C'
|
500
|
-
$diff.format = :context
|
501
|
-
when '-U'
|
502
|
-
$diff.format = :unified
|
503
|
-
when '-e'
|
504
|
-
$diff.format = :ed
|
505
|
-
when '--normal'
|
506
|
-
$diff.format = :normal
|
507
|
-
when '-n'
|
508
|
-
$diff.format = :rcs
|
509
|
-
when '-y'
|
510
|
-
$diff.format = :side_by_side
|
511
|
-
when '-D'
|
512
|
-
$diff.format = :ifdef
|
513
|
-
else
|
514
|
-
$diff.format = :unknown
|
515
|
-
end
|
516
|
-
$diff.format_flags.push([flag, *val])
|
517
|
-
end
|
518
|
-
|
519
|
-
def set_custom_format_flag(flag, *val)
|
520
|
-
if !$diff.custom_format_p
|
521
|
-
$diff.format_flags.clear
|
522
|
-
$diff.custom_format_p = true
|
523
|
-
end
|
524
|
-
$diff.format = :custom
|
525
|
-
$diff.format_flags.push([flag, *val])
|
526
|
-
end
|
527
|
-
|
528
|
-
def diff_main
|
529
|
-
invoke_pager
|
530
|
-
|
531
|
-
$status = 0
|
532
|
-
|
533
|
-
$diff.from_files.each { |from_file|
|
534
|
-
if File.directory?(from_file)
|
535
|
-
$diff.to_files.each { |to_file|
|
536
|
-
if File.directory?(to_file)
|
537
|
-
if $diff.relative
|
538
|
-
to_file = File.expand_path(from_file, to_file)
|
539
|
-
end
|
540
|
-
|
541
|
-
diff_dirs(from_file, to_file)
|
542
|
-
else
|
543
|
-
if $diff.relative
|
544
|
-
from_file = File.expand_path(to_file, from_file)
|
545
|
-
else
|
546
|
-
from_file = File.expand_path(File.basename(to_file), from_file)
|
547
|
-
end
|
548
|
-
|
549
|
-
diff_files(from_file, to_file)
|
550
|
-
end
|
551
|
-
}
|
552
|
-
else
|
553
|
-
$diff.to_files.each { |to_file|
|
554
|
-
if File.directory?(to_file)
|
555
|
-
if $diff.relative
|
556
|
-
to_file = File.expand_path(from_file, to_file)
|
557
|
-
else
|
558
|
-
to_file = File.expand_path(File.basename(from_file), to_file)
|
559
|
-
end
|
560
|
-
end
|
561
|
-
|
562
|
-
diff_files(from_file, to_file)
|
563
|
-
}
|
564
|
-
end
|
565
|
-
}
|
566
|
-
end
|
567
|
-
|
568
|
-
def diff_files(file1, file2)
|
569
|
-
if file1.is_a?(Array)
|
570
|
-
file2.is_a?(Array) and raise "cannot compare two sets of multiple files"
|
571
|
-
file1.empty? and return 0
|
572
|
-
|
573
|
-
call_diff('--to-file', file2, '--', file1)
|
574
|
-
elsif file2.is_a?(Array)
|
575
|
-
file1.empty? and return 0
|
576
|
-
|
577
|
-
call_diff('--from-file', file1, '--', file2)
|
578
|
-
else
|
579
|
-
call_diff('--', file1, file2)
|
580
|
-
end
|
581
|
-
end
|
582
|
-
|
583
|
-
def call_diff(*args)
|
584
|
-
command_args = [DIFF_CMD, $diff.flags, args].flatten
|
585
|
-
if $diff.colorize
|
586
|
-
case $diff.format
|
587
|
-
when :unified
|
588
|
-
filter = method(:colorize_unified_diff)
|
589
|
-
when :context
|
590
|
-
filter = method(:colorize_context_diff)
|
591
|
-
end
|
592
|
-
end
|
593
|
-
if filter
|
594
|
-
require 'shellwords'
|
595
|
-
filter.call(IO.popen(command_args.shelljoin, 'r'))
|
596
|
-
else
|
597
|
-
system(*command_args)
|
598
|
-
end
|
599
|
-
status = $? >> 8
|
600
|
-
$status = status if $status < status
|
601
|
-
return status
|
602
|
-
end
|
603
|
-
|
604
|
-
def diff_dirs(dir1, dir2)
|
605
|
-
entries1 = diff_entries(dir1)
|
606
|
-
entries2 = diff_entries(dir2)
|
607
|
-
|
608
|
-
common = entries1 & entries2
|
609
|
-
missing1 = entries2 - entries1
|
610
|
-
missing2 = entries1 - entries2
|
611
|
-
|
612
|
-
files = []
|
613
|
-
common.each { |file|
|
614
|
-
file1 = File.join(dir1, file)
|
615
|
-
file2 = File.join(dir2, file)
|
616
|
-
file1_is_dir = File.directory?(file1)
|
617
|
-
file2_is_dir = File.directory?(file2)
|
618
|
-
if file1_is_dir && file2_is_dir
|
619
|
-
diff_dirs(file1, file2) if $diff.recursive
|
620
|
-
elsif !file1_is_dir && !file2_is_dir
|
621
|
-
files << file1
|
622
|
-
else
|
623
|
-
missing1 << file
|
624
|
-
missing2 << file
|
625
|
-
end
|
626
|
-
}
|
627
|
-
diff_files(files, dir2)
|
628
|
-
|
629
|
-
[[dir1, missing2], [dir2, missing1]].each { |dir, missing|
|
630
|
-
new_files = []
|
631
|
-
missing.each { |entry|
|
632
|
-
file = File.join(dir, entry)
|
633
|
-
|
634
|
-
if $diff.new_file
|
635
|
-
if File.directory?(file)
|
636
|
-
if dir.equal?(dir1)
|
637
|
-
diff_dirs(file, nil)
|
638
|
-
else
|
639
|
-
diff_dirs(nil, file)
|
640
|
-
end
|
641
|
-
else
|
642
|
-
new_files << file
|
643
|
-
end
|
644
|
-
else
|
645
|
-
printf "Only in %s: %s (%s)\n",
|
646
|
-
dir, entry, File.directory?(file) ? 'directory' : 'file'
|
647
|
-
$status = 1 if $status < 1
|
648
|
-
end
|
649
|
-
}
|
650
|
-
if dir.equal?(dir1)
|
651
|
-
diff_files(new_files, EMPTYFILE)
|
652
|
-
else
|
653
|
-
diff_files(EMPTYFILE, new_files)
|
654
|
-
end
|
655
|
-
}
|
656
|
-
end
|
657
|
-
|
658
|
-
def diff_entries(dir)
|
659
|
-
return [] if dir.nil?
|
660
|
-
return Dir.entries(dir).reject { |file| diff_exclude?(file) }
|
661
|
-
rescue => e
|
662
|
-
warn "#{dir}: #{e}"
|
663
|
-
return []
|
664
|
-
end
|
665
|
-
|
666
|
-
def diff_exclude?(basename)
|
667
|
-
return true if basename == '.' || basename == '..'
|
668
|
-
return false if $diff.include.any? { |pat|
|
669
|
-
File.fnmatch(pat, basename, File::FNM_DOTMATCH)
|
670
|
-
}
|
671
|
-
return true if $diff.exclude.any? { |pat|
|
672
|
-
File.fnmatch(pat, basename, File::FNM_DOTMATCH)
|
673
|
-
}
|
674
|
-
return true if $diff.rsync_exclude && RSYNC_EXCLUDE_GLOBS.any? { |pat|
|
675
|
-
File.fnmatch(pat, basename, File::FNM_DOTMATCH)
|
676
|
-
}
|
677
|
-
return true if $diff.fignore_exclude && FIGNORE_GLOBS.any? { |pat|
|
678
|
-
File.fnmatch(pat, basename, File::FNM_DOTMATCH)
|
679
|
-
}
|
680
|
-
return false
|
681
|
-
end
|
682
|
-
|
683
|
-
def colorize_unified_diff(io)
|
684
|
-
colors = $diff.colors
|
685
|
-
|
686
|
-
state = :comment
|
687
|
-
hunk_left = nil
|
688
|
-
io.each_line { |line|
|
689
|
-
case state
|
690
|
-
when :comment
|
691
|
-
case line
|
692
|
-
when /^\+{3} /
|
693
|
-
color = colors[:file1]
|
694
|
-
when /^-{3} /
|
695
|
-
color = colors[:file2]
|
696
|
-
when /^@@ -[0-9]+(,([0-9]+))? \+[0-9]+(,([0-9]+))?/
|
697
|
-
state = :hunk
|
698
|
-
hunk_left = ($1 ? $2.to_i : 1) + ($3 ? $4.to_i : 1)
|
699
|
-
line.sub!(/^(@@ .*? @@)( )?/) {
|
700
|
-
$1 + ($2 ? colors[:off] + $2 + colors[:function] : '')
|
701
|
-
}
|
702
|
-
color = colors[:header]
|
703
|
-
else
|
704
|
-
color = colors[:comment]
|
705
|
-
end
|
706
|
-
when :hunk
|
707
|
-
check = false
|
708
|
-
case line
|
709
|
-
when /^\+/
|
710
|
-
color = colors[:new]
|
711
|
-
hunk_left -= 1
|
712
|
-
check = $diff.highlight_whitespace
|
713
|
-
when /^-/
|
714
|
-
color = colors[:old]
|
715
|
-
hunk_left -= 1
|
716
|
-
check = $diff.highlight_whitespace
|
717
|
-
when /^ /
|
718
|
-
color = colors[:unchanged]
|
719
|
-
hunk_left -= 2
|
720
|
-
else
|
721
|
-
# error
|
722
|
-
color = colors[:comment]
|
723
|
-
end
|
724
|
-
if check
|
725
|
-
line.sub!(/([ \t]+)$/) {
|
726
|
-
colors[:off] + colors[:whitespace] + $1
|
727
|
-
}
|
728
|
-
end
|
729
|
-
if hunk_left <= 0
|
730
|
-
state = :comment
|
731
|
-
hunk_left = nil
|
732
|
-
end
|
733
|
-
end
|
734
|
-
|
735
|
-
line.sub!(/^/, color)
|
736
|
-
line.sub!(/$/, colors[:off])
|
737
|
-
|
738
|
-
print line
|
739
|
-
}
|
740
|
-
|
741
|
-
io.close
|
742
|
-
end
|
743
|
-
|
744
|
-
def colorize_context_diff(io)
|
745
|
-
colors = $diff.colors
|
746
|
-
|
747
|
-
state = :comment
|
748
|
-
hunk_part = nil
|
749
|
-
io.each_line { |line|
|
750
|
-
case state
|
751
|
-
when :comment
|
752
|
-
case line
|
753
|
-
when /^\*{3} /
|
754
|
-
color = colors[:file1]
|
755
|
-
when /^-{3} /
|
756
|
-
color = colors[:file2]
|
757
|
-
when /^\*{15}/
|
758
|
-
state = :hunk
|
759
|
-
hunk_part = 0
|
760
|
-
line.sub!(/^(\*{15})( )?/) {
|
761
|
-
$1 + ($2 ? colors[:off] + $2 + colors[:function] : '')
|
762
|
-
}
|
763
|
-
color = colors[:header]
|
764
|
-
end
|
765
|
-
when :hunk
|
766
|
-
case hunk_part
|
767
|
-
when 0
|
768
|
-
case line
|
769
|
-
when /^\*{3} /
|
770
|
-
hunk_part = 1
|
771
|
-
color = colors[:header]
|
772
|
-
else
|
773
|
-
# error
|
774
|
-
color = colors[:comment]
|
775
|
-
end
|
776
|
-
when 1, 2
|
777
|
-
check = false
|
778
|
-
case line
|
779
|
-
when /^\-{3} /
|
780
|
-
if hunk_part == 1
|
781
|
-
hunk_part = 2
|
782
|
-
color = colors[:header]
|
783
|
-
else
|
784
|
-
#error
|
785
|
-
color = colors[:comment]
|
786
|
-
end
|
787
|
-
when /^\*{3} /, /^\*{15} /
|
788
|
-
state = :comment
|
789
|
-
redo
|
790
|
-
when /^\+ /
|
791
|
-
color = colors[:new]
|
792
|
-
check = $diff.highlight_whitespace
|
793
|
-
when /^- /
|
794
|
-
color = colors[:old]
|
795
|
-
check = $diff.highlight_whitespace
|
796
|
-
when /^! /
|
797
|
-
color = colors[:changed]
|
798
|
-
check = $diff.highlight_whitespace
|
799
|
-
when /^ /
|
800
|
-
color = colors[:unchanged]
|
801
|
-
else
|
802
|
-
# error
|
803
|
-
color = colors[:comment]
|
804
|
-
end
|
805
|
-
if check
|
806
|
-
line.sub!(/^(...*)([ \t]+)$/) {
|
807
|
-
$1 + colors[:off] + colors[:whitespace] + $2
|
808
|
-
}
|
809
|
-
end
|
810
|
-
end
|
811
|
-
end
|
812
|
-
|
813
|
-
line.sub!(/^/, color)
|
814
|
-
line.sub!(/$/, colors[:off])
|
815
|
-
|
816
|
-
print line
|
817
|
-
}
|
818
|
-
|
819
|
-
io.close
|
3
|
+
begin
|
4
|
+
require 'di'
|
5
|
+
rescue LoadError
|
6
|
+
require 'rubygems'
|
7
|
+
require 'di'
|
820
8
|
end
|
821
9
|
|
822
|
-
main(ARGV)
|
10
|
+
main(ARGV)
|