1#!ruby 2require "time" 3 4def usage! 5 STDERR.puts <<-EOS 6Usage: #$0 [--trunk=<dir>] [--target=<dir>] <revision(s)> 7 8Generate ChangeLog entries for backporting. 9The entries are output to STDOUT, and the messages of this tool are output to 10STDERR. So you can simply redirect STDOUT to get the entries. 11 12You should specify the path of trunk by `--trunk`. If not, assumed cwd. 13You also should specify the path of the target branch by `--target`. If not, 14assumed cwd. 15This means that you have to specify at least one of `--trunk` or `--target`. 16 17revision(s) can be below or their combinations: 18 12345 # means r12345 19 12345,54321 # means r12345 and r54321 20 12345-12347 # means r12345, r12346 and r12347 (of course, if available) 21 22Note that the revisions is backported branch's ones, not trunk's. 23 24The target of this tool is *not* to generate ChangeLog automatically, but to 25generate the draft of ChangeLog. 26You have to check and modify the output. 27 EOS 28 exit 29end 30 31Majors = { 32 "eregon" => "Benoit Daloze <eregontp@gmail.com>", 33 "kazu" => "Kazuhiro NISHIYAMA <zn@mbf.nifty.com>", 34 "ko1" => "Koichi Sasada <ko1@atdot.net>", 35 "marcandre" => "Marc-Andre Lafortune <ruby-core@marc-andre.ca>", 36 "naruse" => "NARUSE, Yui <naruse@ruby-lang.org>", 37 "nobu" => "Nobuyoshi Nakada <nobu@ruby-lang.org>", 38 "normal" => "Eric Wong <normalperson@yhbt.net>", 39 "rhe" => "Kazuki Yamaguchi <k@rhe.jp>", 40 "shugo" => "Shugo Maeda <shugo@ruby-lang.org>", 41 "stomar" => "Marcus Stollsteimer <sto.mar@web.de>", 42 "usa" => "NAKAMURA Usaku <usa@ruby-lang.org>", 43 "zzak" => "Zachary Scott <e@zzak.io>", 44} 45 46trunk = "." 47target = "." 48ARGV.delete_if{|e| /^--trunk=(.*)/ =~ e && trunk = $1} 49ARGV.delete_if{|e| /^--target=(.*)/ =~ e && target = $1} 50usage! if ARGV.size == 0 || trunk == target 51 52revisions = [] 53ARGV.each do |a| 54 a.split(/,/).each do |b| 55 if /-/ =~ b 56 revisions += Range.new(*b.split(/-/, 2).map{|e| Integer(e)}).to_a 57 else 58 revisions << Integer(b) 59 end 60 end 61end 62revisions.sort! 63revisions.reverse! 64 65revisions.each do |rev| 66 if /^Index: ChangeLog$/ =~ `svn diff -c #{rev} #{target}` 67 STDERR.puts "#{rev} already has ChangeLog. Skip." 68 else 69 lines = `svn log -r #{rev} #{target}`.lines[1..-2] 70 if lines.empty? 71 STDERR.puts "#{rev} does not exist. Skip." 72 next 73 end 74 unless /^merge revision\(s\) (\d+)/ =~ lines[2] 75 STDERR.puts "#{rev} is not seems to be a merge commit. Skip." 76 next 77 end 78 original = $1 79 committer = `svn log -r #{original} #{trunk}`.lines[1].split(/\|/)[1].strip 80 if Majors[committer] 81 committer = Majors[committer] 82 else 83 committer = "#{committer} <#{committer}@ruby-lang.org>" 84 end 85 time = Time.parse(lines.shift.split(/\|/)[2]).getlocal("+09:00") 86 puts "#{time.asctime} #{committer}" 87 puts 88 lines.shift(2) # skip "merge" line 89 lines.shift while lines.first == "\n" 90 lines.pop while lines.last == "\n" 91 lines.each do |line| 92 line.chomp! 93 line = "\t#{line}" if line[0] != "\t" && line != "" 94 puts line 95 end 96 puts 97 STDERR.puts "#{rev} is processed." 98 end 99end 100