-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfile2lastrev.rb
executable file
·153 lines (135 loc) · 3.55 KB
/
file2lastrev.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#!/usr/bin/env ruby
ENV.delete('PWD')
require 'optparse'
unless File.respond_to? :realpath
require 'pathname'
def File.realpath(arg)
Pathname(arg).realpath.to_s
end
end
Program = $0
class VCS
class NotFoundError < RuntimeError; end
@@dirs = []
def self.register(dir)
@@dirs << [dir, self]
end
def self.detect(path)
@@dirs.sort.reverse_each do |dir, klass|
return klass.new(path) if File.directory?("#{path}/#{dir}")
end
raise VCS::NotFoundError, "does not seem to be under a vcs: #{path}"
end
def initialize(path)
@srcdir = path
super()
end
# return a pair of strings, the last revision and the last revision in which
# +path+ was modified.
def get_revisions(path)
path = relative_to(path)
last, changed, *rest = Dir.chdir(@srcdir) {self.class.get_revisions(path)}
last or raise "last revision not found"
changed or raise "changed revision not found"
return last, changed, *rest
end
def relative_to(path)
if path
srcdir = File.realpath(@srcdir)
path = File.realpath(path)
list1 = srcdir.split(%r{/})
list2 = path.split(%r{/})
while !list1.empty? && !list2.empty? && list1.first == list2.first
list1.shift
list2.shift
end
if list1.empty? && list2.empty?
"."
else
([".."] * list1.length + list2).join("/")
end
else
'.'
end
end
class SVN < self
register(".svn")
def self.get_revisions(path)
begin
nulldevice = %w[/dev/null NUL NIL: NL:].find {|dev| File.exist?(dev)}
if nulldevice
save_stderr = STDERR.dup
STDERR.reopen nulldevice, 'w'
end
info_xml = `svn info --xml "#{path}"`
ensure
if save_stderr
STDERR.reopen save_stderr
save_stderr.close
end
end
_, last, _, changed, _ = info_xml.split(/revision="(\d+)"/)
[last, changed]
end
end
class GIT < self
register(".git")
def self.get_revisions(path)
logcmd = %Q[git log -n1 --grep="^ *git-svn-id: .*@[0-9][0-9]* "]
idpat = /git-svn-id: .*?@(\d+) \S+\Z/
last = `#{logcmd}`[idpat, 1]
changed = path ? `#{logcmd} "#{path}"`[idpat, 1] : last
[last, changed]
end
end
end
@output = nil
def self.output=(output)
if @output and @output != output
raise "you can specify only one of --changed, --revision.h and --doxygen"
end
@output = output
end
@suppress_not_found = false
srcdir = nil
parser = OptionParser.new {|opts|
opts.on("--srcdir=PATH", "use PATH as source directory") do |path|
srcdir = path
end
opts.on("--changed", "changed rev") do
self.output = :changed
end
opts.on("--revision.h", "RUBY_REVISION macro") do
self.output = :revision_h
end
opts.on("--doxygen", "Doxygen format") do
self.output = :doxygen
end
opts.on("-q", "--suppress_not_found") do
@suppress_not_found = true
end
}
parser.parse! rescue abort "#{File.basename(Program)}: #{$!}\n#{parser}"
srcdir = srcdir ? srcdir : File.dirname(File.dirname(Program))
begin
vcs = VCS.detect(srcdir)
rescue VCS::NotFoundError => e
abort "#{File.basename(Program)}: #{e.message}" unless @suppress_not_found
else
begin
last, changed = vcs.get_revisions(ARGV.shift)
rescue => e
abort "#{File.basename(Program)}: #{e.message}" unless @suppress_not_found
exit false
end
end
case @output
when :changed, nil
puts changed
when :revision_h
puts "#define RUBY_REVISION #{changed.to_i}"
when :doxygen
puts "r#{changed}/r#{last}"
else
raise "unknown output format `#{@output}'"
end