di 0.1.1
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/History.txt +3 -0
- data/README.txt +67 -0
- data/bin/di +545 -0
- metadata +55 -0
data/History.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
= di
|
2
|
+
|
3
|
+
* http://www.idaemons.org/projects/di/
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
di - a wrapper around GNU diff(1)
|
8
|
+
|
9
|
+
== FEATURES:
|
10
|
+
|
11
|
+
The di(1) command wraps around GNU diff(1) to provide reasonable
|
12
|
+
default settings and some original features:
|
13
|
+
|
14
|
+
- Ignore non-significant files, such as backup files, object files,
|
15
|
+
VCS administration files, etc. a la rsync(1)'s --cvs-ignore
|
16
|
+
option.
|
17
|
+
|
18
|
+
- Ignore difference of lines containing RCS tags.
|
19
|
+
|
20
|
+
- Output in unified format.
|
21
|
+
|
22
|
+
- Perform recursive comparison.
|
23
|
+
|
24
|
+
- Turn on some other useful options: -N -p -d.
|
25
|
+
|
26
|
+
- Provide the way to negate any of the above options.
|
27
|
+
|
28
|
+
== SYNOPSIS:
|
29
|
+
|
30
|
+
Run di --help for help.
|
31
|
+
|
32
|
+
== REQUIREMENTS:
|
33
|
+
|
34
|
+
- Ruby 1.8.6 or later
|
35
|
+
|
36
|
+
- GNU diff(1)
|
37
|
+
|
38
|
+
== INSTALL:
|
39
|
+
|
40
|
+
gem install di
|
41
|
+
|
42
|
+
== LICENSE:
|
43
|
+
|
44
|
+
Copyright (c) 2008 Akinori MUSHA
|
45
|
+
|
46
|
+
All rights reserved.
|
47
|
+
|
48
|
+
Redistribution and use in source and binary forms, with or without
|
49
|
+
modification, are permitted provided that the following conditions
|
50
|
+
are met:
|
51
|
+
1. Redistributions of source code must retain the above copyright
|
52
|
+
notice, this list of conditions and the following disclaimer.
|
53
|
+
2. Redistributions in binary form must reproduce the above copyright
|
54
|
+
notice, this list of conditions and the following disclaimer in the
|
55
|
+
documentation and/or other materials provided with the distribution.
|
56
|
+
|
57
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
58
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
59
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
60
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
61
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
62
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
63
|
+
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
64
|
+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
65
|
+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
66
|
+
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
67
|
+
SUCH DAMAGE.
|
data/bin/di
ADDED
@@ -0,0 +1,545 @@
|
|
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 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
|
+
#
|
31
|
+
# $Id: di 69 2008-10-06 03:58:09Z knu $
|
32
|
+
|
33
|
+
MYVERSION = "0.1.1"
|
34
|
+
MYREVISION = %w$Rev: 69 $[1]
|
35
|
+
MYDATE = %w$Date: 2008-10-06 12:58:09 +0900 (Mon, 06 Oct 2008) $[1]
|
36
|
+
MYNAME = File.basename($0)
|
37
|
+
|
38
|
+
DIFF_CMD = ENV.fetch('DIFF', 'diff')
|
39
|
+
EMPTYFILE = '/dev/null'
|
40
|
+
|
41
|
+
RSYNC_EXCLUDE_GLOBS = %w(
|
42
|
+
RCS SCCS CVS CVS.adm
|
43
|
+
RCSLOG cvslog.* tags TAGS
|
44
|
+
.make.state .nse_depinfo *~
|
45
|
+
\#* .\#* ,* _$*
|
46
|
+
*$ *.old *.bak *.BAK
|
47
|
+
*.orig *.rej *.del-* *.a
|
48
|
+
*.olb *.o *.obj *.so
|
49
|
+
*.exe *.Z *.elc *.ln
|
50
|
+
core .svn .git .bzr .hg
|
51
|
+
)
|
52
|
+
|
53
|
+
FIGNORE_GLOBS = ENV.fetch('FIGNORE', '').split(':').map { |pat|
|
54
|
+
'*' + pat
|
55
|
+
}
|
56
|
+
|
57
|
+
def main(args)
|
58
|
+
setup
|
59
|
+
|
60
|
+
parse_args!(args)
|
61
|
+
|
62
|
+
diff_main
|
63
|
+
|
64
|
+
exit $status
|
65
|
+
end
|
66
|
+
|
67
|
+
def warn(*lines)
|
68
|
+
lines.each { |line|
|
69
|
+
STDERR.puts "#{MYNAME}: #{line}"
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
def setup
|
74
|
+
require 'ostruct'
|
75
|
+
$diff = OpenStruct.new
|
76
|
+
$diff.exclude = []
|
77
|
+
$diff.include = []
|
78
|
+
$diff.flags = []
|
79
|
+
end
|
80
|
+
|
81
|
+
def parse_args!(args)
|
82
|
+
require 'optparse'
|
83
|
+
|
84
|
+
banner = <<-"EOF"
|
85
|
+
#{MYNAME} - a wrapper around GNU diff(1)
|
86
|
+
version #{MYVERSION} [revision #{MYREVISION}] (#{MYDATE})
|
87
|
+
|
88
|
+
usage: #{MYNAME} [flags] [files]
|
89
|
+
EOF
|
90
|
+
|
91
|
+
opts = OptionParser.new(banner) { |opts|
|
92
|
+
miniTrueClass = Class.new
|
93
|
+
hash = OptionParser::CompletingHash.new
|
94
|
+
hash['-'] = false
|
95
|
+
opts.accept(miniTrueClass, hash) {|arg, val| val == nil or val}
|
96
|
+
|
97
|
+
opts.on('--[no-]rsync-exclude', '--[no-]cvs-exclude',
|
98
|
+
'Exclude some kinds of files and directories a la rsync(1). [!][*]') { |val|
|
99
|
+
$diff.rsync_exclude = val
|
100
|
+
}
|
101
|
+
opts.on('--[no-]ignore-cvs-lines',
|
102
|
+
'Ignore CVS keyword lines. [!][*]') { |val|
|
103
|
+
$diff.ignore_cvs_lines = val
|
104
|
+
}
|
105
|
+
opts.on('--[no-]fignore-exclude',
|
106
|
+
'Ignore files having suffixes specified in FIGNORE. [!][*]') { |val|
|
107
|
+
$diff.fignore_exclude = val
|
108
|
+
}
|
109
|
+
opts.on('-R', '--relative[=-]', miniTrueClass,
|
110
|
+
'Use relative path names. [*]') { |val|
|
111
|
+
$diff.relative = val
|
112
|
+
}
|
113
|
+
opts.on('-i', '--ignore-case[=-]', miniTrueClass,
|
114
|
+
'Ignore case differences in file contents.') { |val|
|
115
|
+
set_flag('-i', val)
|
116
|
+
}
|
117
|
+
# not supported (yet)
|
118
|
+
#opts.on("--[no-]ignore-file-name-case",
|
119
|
+
# "Ignore case when comparing file names.") { |val|
|
120
|
+
# set_flag("--ignore-file-name-case", val)
|
121
|
+
#}
|
122
|
+
opts.on('-E', '--ignore-tab-expansion[=-]', miniTrueClass,
|
123
|
+
'Ignore changes due to tab expansion.') { |val|
|
124
|
+
set_flag('-E', val)
|
125
|
+
}
|
126
|
+
opts.on('-b', '--ignore-space-change[=-]', miniTrueClass,
|
127
|
+
'Ignore changes in the amount of white space.') { |val|
|
128
|
+
set_flag('-b', val)
|
129
|
+
}
|
130
|
+
opts.on('-w', '--ignore-all-space[=-]', miniTrueClass,
|
131
|
+
'Ignore all white space.') { |val|
|
132
|
+
set_flag('-w', val)
|
133
|
+
}
|
134
|
+
opts.on('-B', '--ignore-blank-lines[=-]', miniTrueClass,
|
135
|
+
'Ignore changes whose lines are all blank.') { |val|
|
136
|
+
set_flag('-B', val)
|
137
|
+
}
|
138
|
+
opts.on('-I RE', '--ignore-matching-lines=RE',
|
139
|
+
'Ignore changes whose lines all match RE.') { |val|
|
140
|
+
set_flag('-I', val)
|
141
|
+
}
|
142
|
+
opts.on('--[no-]strip-trailing-cr',
|
143
|
+
'Strip trailing carriage return on input.') { |val|
|
144
|
+
set_flag('--strip-trailing-cr', val)
|
145
|
+
}
|
146
|
+
opts.on('-a', '--text[=-]', miniTrueClass,
|
147
|
+
'Treat all files as text.') { |val|
|
148
|
+
set_flag('-a', val)
|
149
|
+
}
|
150
|
+
opts.on('-c[NUM]', '--context[=NUM]', Integer,
|
151
|
+
'Output NUM (default 3) lines of copied context.') { |val|
|
152
|
+
$diff.format = ['-C', val ? val.to_s : '3']
|
153
|
+
}
|
154
|
+
opts.on('-C NUM', Integer,
|
155
|
+
'Output NUM lines of copied context.') { |val|
|
156
|
+
$diff.format = ['-C', val.to_s]
|
157
|
+
}
|
158
|
+
opts.on('-u[NUM]', '--unified[=NUM]', Integer,
|
159
|
+
'Output NUM (default 3) lines of unified context. [!]') { |val|
|
160
|
+
$diff.format = ['-U', val ? val.to_s : '3']
|
161
|
+
}
|
162
|
+
opts.on('-U NUM', Integer,
|
163
|
+
'Output NUM lines of unified context.') { |val|
|
164
|
+
$diff.format = ['-U', val.to_s]
|
165
|
+
}
|
166
|
+
opts.on('-L LABEL', '--label=LABEL',
|
167
|
+
'Use LABEL instead of file name.') { |val|
|
168
|
+
set_flag('-L', val)
|
169
|
+
}
|
170
|
+
opts.on('-p', '--show-c-function[=-]', miniTrueClass,
|
171
|
+
'Show which C function each change is in. [!]') { |val|
|
172
|
+
set_flag('-p', val)
|
173
|
+
}
|
174
|
+
opts.on('-F RE', '--show-function-line=RE',
|
175
|
+
'Show the most recent line matching RE.') { |val|
|
176
|
+
set_flag('-F', val)
|
177
|
+
}
|
178
|
+
opts.on('-q', '--brief[=-]', miniTrueClass,
|
179
|
+
'Output only whether files differ.') { |val|
|
180
|
+
set_flag('-q', val)
|
181
|
+
}
|
182
|
+
opts.on('-e', '--ed[=-]', miniTrueClass,
|
183
|
+
'Output an ed script.') { |val|
|
184
|
+
if val
|
185
|
+
$diff.format = ['-e', val]
|
186
|
+
end
|
187
|
+
}
|
188
|
+
opts.on('--normal[=-]', miniTrueClass,
|
189
|
+
'Output a normal diff.') { |val|
|
190
|
+
if val
|
191
|
+
$diff.format = ['--normal', val]
|
192
|
+
end
|
193
|
+
}
|
194
|
+
opts.on('-n', '--rcs[=-]', miniTrueClass,
|
195
|
+
'Output an RCS format diff.') { |val|
|
196
|
+
if val
|
197
|
+
$diff.format = ['-n', val]
|
198
|
+
end
|
199
|
+
}
|
200
|
+
opts.on('-y', '--side-by-side[=-]', miniTrueClass,
|
201
|
+
'Output in two columns.') { |val|
|
202
|
+
if val
|
203
|
+
$diff.format = ['-y', val]
|
204
|
+
end
|
205
|
+
}
|
206
|
+
opts.on('-W NUM', '--width=NUM', Integer,
|
207
|
+
'Output at most NUM (default 130) print columns.') { |val|
|
208
|
+
set_flag('-W', val.to_s)
|
209
|
+
}
|
210
|
+
opts.on('--left-column[=-]', miniTrueClass,
|
211
|
+
'Output only the left column of common lines.') { |val|
|
212
|
+
set_flag('--left-column', val)
|
213
|
+
}
|
214
|
+
opts.on('--suppress-common-lines[=-]', miniTrueClass,
|
215
|
+
'Do not output common lines.') { |val|
|
216
|
+
set_flag('--suppress-common-lines', val)
|
217
|
+
}
|
218
|
+
opts.on('-D NAME', '--ifdef=NAME',
|
219
|
+
'Output merged file to show `#ifdef NAME\' diffs.') { |val|
|
220
|
+
set_flag('-D', val)
|
221
|
+
}
|
222
|
+
opts.on('--old-group-format=GFMT',
|
223
|
+
'Format old input groups with GFMT.') { |val|
|
224
|
+
set_flag('--old-group-format', val)
|
225
|
+
}
|
226
|
+
opts.on('--new-group-format=GFMT',
|
227
|
+
'Format new input groups with GFMT.') { |val|
|
228
|
+
set_flag('--new-group-format', val)
|
229
|
+
}
|
230
|
+
opts.on('--unchanged-group-format=GFMT',
|
231
|
+
'Format unchanged input groups with GFMT.') { |val|
|
232
|
+
set_flag('--unchanged-group-format', val)
|
233
|
+
}
|
234
|
+
opts.on('--line-format=LFMT',
|
235
|
+
'Format all input lines with LFMT.') { |val|
|
236
|
+
set_flag('--line-format', val)
|
237
|
+
}
|
238
|
+
opts.on('--old-line-format=LFMT',
|
239
|
+
'Format old input lines with LFMT.') { |val|
|
240
|
+
set_flag('--old-line-format', val)
|
241
|
+
}
|
242
|
+
opts.on('--new-line-format=LFMT',
|
243
|
+
'Format new input lines with LFMT.') { |val|
|
244
|
+
set_flag('--new-line-format', val)
|
245
|
+
}
|
246
|
+
opts.on('--unchanged-line-format=LFMT',
|
247
|
+
'Format unchanged input lines with LFMT.') { |val|
|
248
|
+
set_flag('--unchanged-line-format', val)
|
249
|
+
}
|
250
|
+
opts.on('-l', '--paginate[=-]', miniTrueClass,
|
251
|
+
'Pass the output through `pr\' to paginate it.') { |val|
|
252
|
+
set_flag('-l', val)
|
253
|
+
}
|
254
|
+
opts.on('-t', '--expand-tabs[=-]', miniTrueClass,
|
255
|
+
'Expand tabs to spaces in output.') { |val|
|
256
|
+
set_flag('-t', val)
|
257
|
+
}
|
258
|
+
opts.on('-T', '--initial-tab[=-]', miniTrueClass,
|
259
|
+
'Make tabs line up by prepending a tab.') { |val|
|
260
|
+
set_flag('-T', '--initial-tab', val)
|
261
|
+
}
|
262
|
+
opts.on('--tabsize=NUM', Integer,
|
263
|
+
'Tab stops are every NUM (default 8) print columns.') { |val|
|
264
|
+
set_flag('--tabsize', val.to_s)
|
265
|
+
}
|
266
|
+
opts.on('-r', '--recursive[=-]', miniTrueClass,
|
267
|
+
'Recursively compare any subdirectories found. [!]') { |val|
|
268
|
+
set_flag('-r', val)
|
269
|
+
$diff.recursive = val
|
270
|
+
}
|
271
|
+
opts.on('-N', '--[no-]new-file[=-]', miniTrueClass,
|
272
|
+
'Treat absent files as empty. [!]') { |val|
|
273
|
+
set_flag('-N', val)
|
274
|
+
$diff.new_file = val
|
275
|
+
}
|
276
|
+
opts.on('--unidirectional-new-file[=-]', miniTrueClass,
|
277
|
+
'Treat absent first files as empty.') { |val|
|
278
|
+
set_flag('--unidirectional-new-file', val)
|
279
|
+
}
|
280
|
+
opts.on('-s', '--report-identical-files[=-]', miniTrueClass,
|
281
|
+
'Report when two files are the same.') { |val|
|
282
|
+
set_flag('-s', val)
|
283
|
+
}
|
284
|
+
opts.on('-x PAT', '--exclude=PAT',
|
285
|
+
'Exclude files that match PAT.') { |val|
|
286
|
+
$diff.exclude << val
|
287
|
+
}
|
288
|
+
opts.on('-X FILE', '--exclude-from=FILE',
|
289
|
+
'Exclude files that match any pattern in FILE.') { |val|
|
290
|
+
if val == '-'
|
291
|
+
$diff.exclude.concat(STDIN.read.split(/\n/))
|
292
|
+
else
|
293
|
+
$diff.exclude.concat(File.read(val).split(/\n/))
|
294
|
+
end
|
295
|
+
}
|
296
|
+
opts.on('--include=PAT',
|
297
|
+
'Do not exclude files that match PAT.') { |val|
|
298
|
+
$diff.include << val
|
299
|
+
}
|
300
|
+
opts.on('-S FILE', '--starting-file=FILE',
|
301
|
+
'Start with FILE when comparing directories.') { |val|
|
302
|
+
set_flag('-S', val)
|
303
|
+
}
|
304
|
+
opts.on('--from-file=FILE1',
|
305
|
+
'Compare FILE1 to all operands. FILE1 can be a directory.') { |val|
|
306
|
+
$diff.from_files = [val]
|
307
|
+
}
|
308
|
+
opts.on('--to-file=FILE2',
|
309
|
+
'Compare all operands to FILE2. FILE2 can be a directory.') { |val|
|
310
|
+
$diff.to_files = [val]
|
311
|
+
}
|
312
|
+
opts.on('--horizon-lines=NUM', Integer,
|
313
|
+
'Keep NUM lines of the common prefix and suffix.') { |val|
|
314
|
+
set_flag('--horizon-lines', val.to_s)
|
315
|
+
}
|
316
|
+
opts.on('-d', '--minimal[=-]', miniTrueClass,
|
317
|
+
'Try hard to find a smaller set of changes. [!]') { |val|
|
318
|
+
set_flag('-d', val)
|
319
|
+
}
|
320
|
+
opts.on('--speed-large-files[=-]', miniTrueClass,
|
321
|
+
'Assume large files and many scattered small changes.') { |val|
|
322
|
+
set_flag('--speed-large-files', val)
|
323
|
+
}
|
324
|
+
opts.on('-v', '--version',
|
325
|
+
'Output version info.') { |val|
|
326
|
+
set_flag('-v', val)
|
327
|
+
}
|
328
|
+
opts.on('--help',
|
329
|
+
'Output this help.') { |val|
|
330
|
+
print opts,
|
331
|
+
"\n",
|
332
|
+
"Options without the [*] sign will be passed through to diff(1).\n",
|
333
|
+
"Options marked as [!] sign are turned on by default. To turn them off,\n",
|
334
|
+
"specify -?- for short options and --no-??? for long options, respectively.\n"
|
335
|
+
exit 0
|
336
|
+
}
|
337
|
+
}
|
338
|
+
|
339
|
+
begin
|
340
|
+
opts.parse('--rsync-exclude', '--fignore-exclude', '--ignore-cvs-lines',
|
341
|
+
'-N', '-r', '-p', '-d')
|
342
|
+
opts.parse!(args)
|
343
|
+
|
344
|
+
$diff.format ||= ['-U', '3']
|
345
|
+
set_flag(*$diff.format)
|
346
|
+
|
347
|
+
if $diff.ignore_cvs_lines
|
348
|
+
opts.parse('--ignore-matching-lines=\$[A-Z][A-Za-z0-9][A-Za-z0-9]*\(:.*\)\{0,1\}\$')
|
349
|
+
end
|
350
|
+
rescue OptionParser::ParseError => e
|
351
|
+
warn e, "Try `#{MYNAME} --help' for more information."
|
352
|
+
exit 64
|
353
|
+
rescue => e
|
354
|
+
warn e
|
355
|
+
exit 1
|
356
|
+
end
|
357
|
+
|
358
|
+
begin
|
359
|
+
if $diff.from_files
|
360
|
+
$diff.to_files ||= args.dup
|
361
|
+
|
362
|
+
if $diff.to_files.empty?
|
363
|
+
raise "missing operand"
|
364
|
+
end
|
365
|
+
elsif $diff.to_files
|
366
|
+
$diff.from_files = args.dup
|
367
|
+
|
368
|
+
if $diff.from_files.empty?
|
369
|
+
raise "missing operand"
|
370
|
+
end
|
371
|
+
else
|
372
|
+
if args.size < 2
|
373
|
+
raise "missing operand"
|
374
|
+
end
|
375
|
+
|
376
|
+
if File.directory?(args[0])
|
377
|
+
$diff.to_files = args.dup
|
378
|
+
$diff.from_files = [$diff.to_files.shift]
|
379
|
+
else
|
380
|
+
$diff.from_files = args.dup
|
381
|
+
$diff.to_files = [$diff.from_files.pop]
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
if $diff.from_files.size != 1 && $diff.to_files.size != 1
|
386
|
+
raise "wrong number of files given"
|
387
|
+
end
|
388
|
+
rescue => e
|
389
|
+
warn e, "Try `#{MYNAME} --help' for more information."
|
390
|
+
exit 64
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
def set_flag(flag, val)
|
395
|
+
case val
|
396
|
+
when false
|
397
|
+
$diff.flags.reject! { |f,| f == flag }
|
398
|
+
when true
|
399
|
+
$diff.flags.reject! { |f,| f == flag }
|
400
|
+
$diff.flags << [flag]
|
401
|
+
else
|
402
|
+
$diff.flags << [flag, val]
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
def diff_main
|
407
|
+
$status = 0
|
408
|
+
|
409
|
+
$diff.from_files.each { |from_file|
|
410
|
+
if File.directory?(from_file)
|
411
|
+
$diff.to_files.each { |to_file|
|
412
|
+
if File.directory?(to_file)
|
413
|
+
if $diff.relative
|
414
|
+
to_file = File.expand_path(from_file, to_file)
|
415
|
+
end
|
416
|
+
|
417
|
+
diff_dirs(from_file, to_file)
|
418
|
+
else
|
419
|
+
if $diff.relative
|
420
|
+
from_file = File.expand_path(to_file, from_file)
|
421
|
+
else
|
422
|
+
from_file = File.expand_path(File.basename(to_file), from_file)
|
423
|
+
end
|
424
|
+
|
425
|
+
diff_files(from_file, to_file)
|
426
|
+
end
|
427
|
+
}
|
428
|
+
else
|
429
|
+
$diff.to_files.each { |to_file|
|
430
|
+
if File.directory?(to_file)
|
431
|
+
if $diff.relative
|
432
|
+
to_file = File.expand_path(from_file, to_file)
|
433
|
+
else
|
434
|
+
to_file = File.expand_path(File.basename(from_file), to_file)
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
diff_files(from_file, to_file)
|
439
|
+
}
|
440
|
+
end
|
441
|
+
}
|
442
|
+
end
|
443
|
+
|
444
|
+
def diff_files(file1, file2)
|
445
|
+
if file1.is_a?(Array)
|
446
|
+
file2.is_a?(Array) and raise "cannot compare two sets of multiple files"
|
447
|
+
file1.empty? and return 0
|
448
|
+
|
449
|
+
call_diff('--to-file', file2, file1)
|
450
|
+
elsif file2.is_a?(Array)
|
451
|
+
file1.empty? and return 0
|
452
|
+
|
453
|
+
call_diff('--from-file', file1, file2)
|
454
|
+
else
|
455
|
+
call_diff(file1, file2)
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
459
|
+
def call_diff(*args)
|
460
|
+
system(*[DIFF_CMD, $diff.flags, args].flatten)
|
461
|
+
status = $? >> 8
|
462
|
+
$status = status if $status < status
|
463
|
+
return status
|
464
|
+
end
|
465
|
+
|
466
|
+
def diff_dirs(dir1, dir2)
|
467
|
+
entries1 = diff_entries(dir1)
|
468
|
+
entries2 = diff_entries(dir2)
|
469
|
+
|
470
|
+
common = entries1 & entries2
|
471
|
+
missing1 = entries2 - entries1
|
472
|
+
missing2 = entries1 - entries2
|
473
|
+
|
474
|
+
files = []
|
475
|
+
common.each { |file|
|
476
|
+
file1 = File.join(dir1, file)
|
477
|
+
file2 = File.join(dir2, file)
|
478
|
+
file1_is_dir = File.directory?(file1)
|
479
|
+
file2_is_dir = File.directory?(file2)
|
480
|
+
if file1_is_dir && file2_is_dir
|
481
|
+
diff_dirs(file1, file2) if $diff.recursive
|
482
|
+
elsif !file1_is_dir && !file2_is_dir
|
483
|
+
files << file1
|
484
|
+
else
|
485
|
+
missing1 << file
|
486
|
+
missing2 << file
|
487
|
+
end
|
488
|
+
}
|
489
|
+
diff_files(files, dir2)
|
490
|
+
|
491
|
+
[[dir1, missing2], [dir2, missing1]].each { |dir, missing|
|
492
|
+
new_files = []
|
493
|
+
missing.each { |entry|
|
494
|
+
file = File.join(dir, entry)
|
495
|
+
|
496
|
+
if $diff.new_file
|
497
|
+
if File.directory?(file)
|
498
|
+
if dir.equal?(dir1)
|
499
|
+
diff_dirs(file, nil)
|
500
|
+
else
|
501
|
+
diff_dirs(nil, file)
|
502
|
+
end
|
503
|
+
else
|
504
|
+
new_files << file
|
505
|
+
end
|
506
|
+
else
|
507
|
+
printf "Only in %s: %s (%s)\n",
|
508
|
+
dir, entry, File.directory?(file) ? 'directory' : 'file'
|
509
|
+
$status = 1 if $status < 1
|
510
|
+
end
|
511
|
+
}
|
512
|
+
if dir.equal?(dir1)
|
513
|
+
diff_files(new_files, EMPTYFILE)
|
514
|
+
else
|
515
|
+
diff_files(EMPTYFILE, new_files)
|
516
|
+
end
|
517
|
+
}
|
518
|
+
end
|
519
|
+
|
520
|
+
def diff_entries(dir)
|
521
|
+
return [] if dir.nil?
|
522
|
+
return Dir.entries(dir).reject { |file| diff_exclude?(file) }
|
523
|
+
rescue => e
|
524
|
+
warn "#{dir}: #{e}"
|
525
|
+
return []
|
526
|
+
end
|
527
|
+
|
528
|
+
def diff_exclude?(basename)
|
529
|
+
return true if basename == '.' || basename == '..'
|
530
|
+
return false if $diff.include.any? { |pat|
|
531
|
+
File.fnmatch(pat, basename, File::FNM_DOTMATCH)
|
532
|
+
}
|
533
|
+
return true if $diff.exclude.any? { |pat|
|
534
|
+
File.fnmatch(pat, basename, File::FNM_DOTMATCH)
|
535
|
+
}
|
536
|
+
return true if $diff.rsync_exclude && RSYNC_EXCLUDE_GLOBS.any? { |pat|
|
537
|
+
File.fnmatch(pat, basename, File::FNM_DOTMATCH)
|
538
|
+
}
|
539
|
+
return true if $diff.fignore_exclude && FIGNORE_GLOBS.any? { |pat|
|
540
|
+
File.fnmatch(pat, basename, File::FNM_DOTMATCH)
|
541
|
+
}
|
542
|
+
return false
|
543
|
+
end
|
544
|
+
|
545
|
+
main(ARGV)
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: di
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Akinori MUSHA
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-10-06 00:00:00 +09:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: knu@idaemons.org
|
18
|
+
executables:
|
19
|
+
- di
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- bin/di
|
26
|
+
- README.txt
|
27
|
+
- History.txt
|
28
|
+
has_rdoc: false
|
29
|
+
homepage: http://www.idaemons.org/projects/di/
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: "0"
|
40
|
+
version:
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
version:
|
47
|
+
requirements: []
|
48
|
+
|
49
|
+
rubyforge_project: unixutils
|
50
|
+
rubygems_version: 1.0.1
|
51
|
+
signing_key:
|
52
|
+
specification_version: 2
|
53
|
+
summary: A wrapper around GNU diff(1)
|
54
|
+
test_files: []
|
55
|
+
|