summaryrefslogtreecommitdiff
path: root/tool/file2lastrev.rb
blob: 057ea80fddb1d4cd290cae788ed366855a365a00 (plain)
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
#!/usr/bin/env ruby

ENV.delete('PWD')

require 'optparse'
require 'pathname'

Program = Pathname($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
  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 = Dir.chdir(@srcdir) {yield path}
    last or raise "last revision not found"
    changed or raise "changed revision not found"
    return last, changed
  end

  def relative_to(path)
    path ? Pathname(path).relative_path_from(@srcdir) : '.'
  end

  class SVN < self
    register(".svn")

    def get_revisions(*)
      super do |path|
        info_xml = `svn info --xml "#{path}"`
        _, last, _, changed, _ = info_xml.split(/revision="(\d+)"/)
        [last, changed]
      end
    end
  end

  class GIT_SVN < self
    register(".git/svn")

    def get_revisions(*)
      super do |path|
        info = `git svn info "#{path}"`
        [info[/^Revision: (\d+)/, 1], info[/^Last Changed Rev: (\d+)/, 1]]
      end
    end
  end

  class GIT < self
    register(".git")

    def get_revisions(*)
      logcmd = %Q[git log -n1 --grep="^ *git-svn-id: .*@[0-9][0-9]* "]
      idpat = /git-svn-id: .*?@(\d+) \S+\Z/
      super do |path|
        last = `#{logcmd}`[idpat, 1]
        changed = path ? `#{logcmd} "#{path}"`[idpat, 1] : last
        [last, changed]
      end
    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 "#{Program.basename}: #{$!}\n#{parser}"

srcdir = (srcdir ? Pathname(srcdir) : Program.parent.parent).freeze
begin
  vcs = VCS.detect(srcdir)
rescue VCS::NotFoundError => e
  abort "#{Program.basename}: #{e.message}" unless @suppress_not_found
else
  last, changed = vcs.get_revisions(ARGV.shift)
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