1#!/bin/sh 2# -*- ruby -*- 3exec "${RUBY-ruby}" "-x" "$0" "$@" && [ ] if false 4#!ruby 5# This needs ruby 1.9 and subversion. 6# run this in a repository to commit. 7 8require 'date' 9require 'tempfile' 10 11$repos = 'svn+ssh://svn@ci.ruby-lang.org/ruby/' 12ENV['LC_ALL'] = 'C' 13 14def help 15 puts <<-end 16simple backport 17 ruby #$0 1234 18 19range backport 20 ruby #$0 1234:5678 21 22backport from other branch 23 ruby #$0 17502 mvm 24 25revision increment 26 ruby #$0 revisionup 27 28tagging patch release 29 ruby #$0 tag 30 31tagging preview/RC 32 ruby #$0 tag 2.0.0-preview1 33 34* all operations shall be applied to the working directory. 35end 36end 37 38def version 39 v = p = nil 40 open 'version.h', 'rb' do |f| 41 f.each_line do |l| 42 case l 43 when /^#define RUBY_VERSION "(\d)\.(\d)\.(\d)"$/ 44 v = $~.captures 45 when /^#define RUBY_PATCHLEVEL (-?\d+)$/ 46 p = $1 47 end 48 end 49 end 50 return v, p 51end 52 53def interactive str, editfile = nil 54 loop do 55 yield 56 STDERR.puts "#{str} ([y]es|[a]bort|[r]etry#{'|[e]dit' if editfile})" 57 case STDIN.gets 58 when /\Aa/i then exit 59 when /\Ar/i then redo 60 when /\Ay/i then break 61 when /\Ae/i then system(ENV["EDITOR"], editfile) 62 else exit 63 end 64 end 65end 66 67def version_up 68 d = DateTime.now 69 d = d.new_offset(Rational(9,24)) # we need server locale (i.e. japanese) time 70 system(*%w'svn revert version.h') 71 v, p = version 72 73 teeny = v[2] 74 case v 75 when %w/1 9 2/ 76 teeny = 1 77 end 78 79 p = p.to_i 80 if p != -1 81 p += 1 82 end 83 84 str = open 'version.h', 'rb' do |f| f.read end 85 [%W[RUBY_VERSION "#{v.join '.'}"], 86 %W[RUBY_VERSION_CODE #{v.join ''}], 87 %W[RUBY_VERSION_MAJOR #{v[0]}], 88 %W[RUBY_VERSION_MINOR #{v[1]}], 89 %W[RUBY_VERSION_TEENY #{teeny}], 90 %W[RUBY_RELEASE_DATE "#{d.strftime '%Y-%m-%d'}"], 91 %W[RUBY_RELEASE_CODE #{d.strftime '%Y%m%d'}], 92 %W[RUBY_PATCHLEVEL #{p}], 93 %W[RUBY_RELEASE_YEAR #{d.year}], 94 %W[RUBY_RELEASE_MONTH #{d.month}], 95 %W[RUBY_RELEASE_DAY #{d.day}], 96 ].each do |(k, i)| 97 str.sub!(/^(#define\s+#{k}\s+).*$/, "\\1#{i}") 98 end 99 str.sub!(/\s+\z/m, '') 100 fn = sprintf 'version.h.tmp.%032b', rand(1 << 31) 101 File.rename 'version.h', fn 102 open 'version.h', 'wb' do |f| 103 f.puts str 104 end 105 File.unlink fn 106end 107 108def tag intv_p = false, relname=nil 109 # relname: 110 # * 2.0.0-preview1 111 # * 2.0.0-rc1 112 # * 2.0.0-p0 113 # * 2.0.0-p100 114 v, pl = version 115 x = v.join('_') 116 if relname 117 abort "patch level is not -1 but '#{pl}' even if this is new release" if pl != '-1' 118 pl = relname[/-(.*)\z/, 1] 119 curver = v.join('.') + '-' + pl 120 if relname != curver 121 abort "geiven relname '#{relname}' conflicts current version '#{curver}'" 122 end 123 branch_url = `svn info`[/URL: (.*)/, 1] 124 else 125 if pl == '-1' 126 abort "no relname is given and not in a release branch even if this is patch release" 127 end 128 branch_url = $repos + 'branches/ruby_' + x 129 end 130 tagname = 'v' + x + '_' + pl 131 tag_url = $repos + 'tags/' + tagname 132 if intv_p 133 interactive "OK? svn cp -m \"add tag #{tagname}\" #{branch_url} #{tag_url}" do 134 end 135 end 136 system(*%w'svn cp -m', "add tag #{tagname}", branch_url, tag_url) 137end 138 139def default_merge_branch 140 %r{^URL: .*/branches/ruby_1_8_} =~ `svn info` ? 'branches/ruby_1_8' : 'trunk' 141end 142 143case ARGV[0] 144when "up", /\A(ver|version|rev|revision|lv|level|patch\s*level)\s*up/ 145 version_up 146 system 'svn diff version.h' 147when "tag" 148 tag :interactive, ARGV[1] 149when nil, "-h", "--help" 150 help 151 exit 152else 153 system 'svn up' 154 155 if /--ticket=(.*)/ =~ ARGV[0] 156 tickets = $1.split(/,/).map{|num| " [Backport ##{num}]"} 157 ARGV.shift 158 else 159 tickets = [] 160 end 161 162 q = $repos + (ARGV[1] || default_merge_branch) 163 revs = ARGV[0].split(/,\s*/) 164 log = '' 165 log_svn = '' 166 167 revs.each do |rev| 168 case rev 169 when /\Ar?\d+:r?\d+\z/ 170 r = ['-r', rev] 171 when /\Ar?\d+\z/ 172 r = ['-c', rev] 173 when nil then 174 puts "#$0 revision" 175 exit 176 end 177 178 l = IO.popen %w'svn diff' + r + %w'--diff-cmd=diff -x -pU0' + [File.join(q, 'ChangeLog')] do |f| 179 f.read 180 end 181 182 log << l 183 log_svn << l.lines.grep(/^\+\t/).join.gsub(/^\+/, '').gsub(/^\t\*/, "\n\t\*") 184 185 if log_svn.empty? 186 log_svn = IO.popen %w'svn log ' + r + [q] do |f| 187 f.read 188 end.sub(/\A-+\nr.*\n/, '').sub(/\n-+\n\z/, '').gsub(/^(?=\S)/, "\t") 189 end 190 191 a = %w'svn merge --accept=postpone' + r + [q] 192 STDERR.puts a.join(' ') 193 194 system(*a) 195 system(*%w'svn revert ChangeLog') if /^\+/ =~ l 196 end 197 198 if `svn diff --diff-cmd=diff -x -upw`.empty? 199 interactive 'Only ChangeLog is modified, right?' do 200 end 201 end 202 203 if /^\+/ =~ log 204 system(*%w'svn revert ChangeLog') 205 IO.popen %w'patch -p0', 'wb' do |f| 206 f.write log.gsub(/\+(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) [ 123][0-9] [012][0-9]:[0-5][0-9]:[0-5][0-9] \d\d\d\d/, 207 # this format-time-string was from the file local variables of ChangeLog 208 '+'+Time.now.strftime('%a %b %e %H:%M:%S %Y')) 209 end 210 system(*%w'touch ChangeLog') # needed somehow, don't know why... 211 else 212 STDERR.puts '*** You should write ChangeLog NOW!!! ***' 213 end 214 215 version_up 216 f = Tempfile.new 'merger.rb' 217 f.printf "merge revision(s) %s:%s\n", ARGV[0], tickets.join 218 f.write log_svn 219 f.flush 220 f.close 221 222 interactive 'conflicts resolved?', f.path do 223 IO.popen(ENV["PAGER"] || "less", "w") do |g| 224 g << `svn stat` 225 g << "\n\n" 226 f.open 227 g << f.read 228 f.close 229 g << "\n\n" 230 g << `svn diff --diff-cmd=diff -x -upw` 231 end 232 end 233 234 if system(*%w'svn ci -F', f.path) 235 # tag :interactive # no longer needed. 236 system 'rm -f subversion.commitlog' 237 else 238 puts 'commit failed; try again.' 239 end 240 241 f.close 242end 243