comparison lib/redmine/scm/adapters/cvs_adapter.rb @ 511:107d36338b70 live

Merge from branch "cannam"
author Chris Cannam
date Thu, 14 Jul 2011 10:43:07 +0100
parents 753f1380d6bc
children 5e80956cc792
comparison
equal deleted inserted replaced
451:a9f6345cb43d 511:107d36338b70
3 # 3 #
4 # This program is free software; you can redistribute it and/or 4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License 5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2 6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version. 7 # of the License, or (at your option) any later version.
8 # 8 #
9 # This program is distributed in the hope that it will be useful, 9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details. 12 # GNU General Public License for more details.
13 # 13 #
14 # You should have received a copy of the GNU General Public License 14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software 15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 require 'redmine/scm/adapters/abstract_adapter' 18 require 'redmine/scm/adapters/abstract_adapter'
21 module Scm 21 module Scm
22 module Adapters 22 module Adapters
23 class CvsAdapter < AbstractAdapter 23 class CvsAdapter < AbstractAdapter
24 24
25 # CVS executable name 25 # CVS executable name
26 CVS_BIN = "cvs" 26 CVS_BIN = Redmine::Configuration['scm_cvs_command'] || "cvs"
27 27
28 # raised if scm command exited with error, e.g. unknown revision.
29 class ScmCommandAborted < CommandFailed; end
30
31 class << self
32 def client_command
33 @@bin ||= CVS_BIN
34 end
35
36 def sq_bin
37 @@sq_bin ||= shell_quote(CVS_BIN)
38 end
39
40 def client_version
41 @@client_version ||= (scm_command_version || [])
42 end
43
44 def client_available
45 client_version_above?([1, 12])
46 end
47
48 def scm_command_version
49 scm_version = scm_version_from_command_line.dup
50 if scm_version.respond_to?(:force_encoding)
51 scm_version.force_encoding('ASCII-8BIT')
52 end
53 if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)}m)
54 m[2].scan(%r{\d+}).collect(&:to_i)
55 end
56 end
57
58 def scm_version_from_command_line
59 shellout("#{sq_bin} --version") { |io| io.read }.to_s
60 end
61 end
62
28 # Guidelines for the input: 63 # Guidelines for the input:
29 # url -> the project-path, relative to the cvsroot (eg. module name) 64 # url -> the project-path, relative to the cvsroot (eg. module name)
30 # root_url -> the good old, sometimes damned, CVSROOT 65 # root_url -> the good old, sometimes damned, CVSROOT
31 # login -> unnecessary 66 # login -> unnecessary
32 # password -> unnecessary too 67 # password -> unnecessary too
33 def initialize(url, root_url=nil, login=nil, password=nil) 68 def initialize(url, root_url=nil, login=nil, password=nil,
34 @url = url 69 path_encoding=nil)
35 @login = login if login && !login.empty? 70 @path_encoding = path_encoding.blank? ? 'UTF-8' : path_encoding
71 @url = url
72 # TODO: better Exception here (IllegalArgumentException)
73 raise CommandFailed if root_url.blank?
74 @root_url = root_url
75
76 # These are unused.
77 @login = login if login && !login.empty?
36 @password = (password || "") if @login 78 @password = (password || "") if @login
37 #TODO: better Exception here (IllegalArgumentException) 79 end
38 raise CommandFailed if root_url.blank? 80
39 @root_url = root_url 81 def path_encoding
40 end 82 @path_encoding
41 83 end
42 def root_url 84
43 @root_url
44 end
45
46 def url
47 @url
48 end
49
50 def info 85 def info
51 logger.debug "<cvs> info" 86 logger.debug "<cvs> info"
52 Info.new({:root_url => @root_url, :lastrev => nil}) 87 Info.new({:root_url => @root_url, :lastrev => nil})
53 end 88 end
54 89
55 def get_previous_revision(revision) 90 def get_previous_revision(revision)
56 CvsRevisionHelper.new(revision).prevRev 91 CvsRevisionHelper.new(revision).prevRev
57 end 92 end
58 93
59 # Returns an Entries collection 94 # Returns an Entries collection
60 # or nil if the given path doesn't exist in the repository 95 # or nil if the given path doesn't exist in the repository
61 # this method is used by the repository-browser (aka LIST) 96 # this method is used by the repository-browser (aka LIST)
62 def entries(path=nil, identifier=nil) 97 def entries(path=nil, identifier=nil, options={})
63 logger.debug "<cvs> entries '#{path}' with identifier '#{identifier}'" 98 logger.debug "<cvs> entries '#{path}' with identifier '#{identifier}'"
64 path_with_project="#{url}#{with_leading_slash(path)}" 99 path_locale = scm_iconv(@path_encoding, 'UTF-8', path)
100 path_locale.force_encoding("ASCII-8BIT") if path_locale.respond_to?(:force_encoding)
65 entries = Entries.new 101 entries = Entries.new
66 cmd = "#{CVS_BIN} -d #{root_url} rls -e" 102 cmd_args = %w|-q rls -e|
67 cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier 103 cmd_args << "-D" << time_to_cvstime_rlog(identifier) if identifier
68 cmd << " #{shell_quote path_with_project}" 104 cmd_args << path_with_proj(path)
69 shellout(cmd) do |io| 105 scm_cmd(*cmd_args) do |io|
70 io.each_line(){|line| 106 io.each_line() do |line|
71 fields=line.chop.split('/',-1) 107 fields = line.chop.split('/',-1)
72 logger.debug(">>InspectLine #{fields.inspect}") 108 logger.debug(">>InspectLine #{fields.inspect}")
73
74 if fields[0]!="D" 109 if fields[0]!="D"
75 entries << Entry.new({:name => fields[-5], 110 time = nil
111 # Thu Dec 13 16:27:22 2007
112 time_l = fields[-3].split(' ')
113 if time_l.size == 5 && time_l[4].length == 4
114 begin
115 time = Time.parse(
116 "#{time_l[1]} #{time_l[2]} #{time_l[3]} GMT #{time_l[4]}")
117 rescue
118 end
119 end
120 entries << Entry.new(
121 {
122 :name => scm_iconv('UTF-8', @path_encoding, fields[-5]),
76 #:path => fields[-4].include?(path)?fields[-4]:(path + "/"+ fields[-4]), 123 #:path => fields[-4].include?(path)?fields[-4]:(path + "/"+ fields[-4]),
77 :path => "#{path}/#{fields[-5]}", 124 :path => scm_iconv('UTF-8', @path_encoding, "#{path_locale}/#{fields[-5]}"),
78 :kind => 'file', 125 :kind => 'file',
79 :size => nil, 126 :size => nil,
80 :lastrev => Revision.new({ 127 :lastrev => Revision.new(
81 :revision => fields[-4], 128 {
82 :name => fields[-4], 129 :revision => fields[-4],
83 :time => Time.parse(fields[-3]), 130 :name => scm_iconv('UTF-8', @path_encoding, fields[-4]),
84 :author => '' 131 :time => time,
132 :author => ''
133 })
85 }) 134 })
86 })
87 else 135 else
88 entries << Entry.new({:name => fields[1], 136 entries << Entry.new(
89 :path => "#{path}/#{fields[1]}", 137 {
90 :kind => 'dir', 138 :name => scm_iconv('UTF-8', @path_encoding, fields[1]),
91 :size => nil, 139 :path => scm_iconv('UTF-8', @path_encoding, "#{path_locale}/#{fields[1]}"),
140 :kind => 'dir',
141 :size => nil,
92 :lastrev => nil 142 :lastrev => nil
93 }) 143 })
94 end 144 end
95 } 145 end
96 end 146 end
97 return nil if $? && $?.exitstatus != 0
98 entries.sort_by_name 147 entries.sort_by_name
99 end 148 rescue ScmCommandAborted
149 nil
150 end
100 151
101 STARTLOG="----------------------------" 152 STARTLOG="----------------------------"
102 ENDLOG ="=============================================================================" 153 ENDLOG ="============================================================================="
103 154
104 # Returns all revisions found between identifier_from and identifier_to 155 # Returns all revisions found between identifier_from and identifier_to
105 # in the repository. both identifier have to be dates or nil. 156 # in the repository. both identifier have to be dates or nil.
106 # these method returns nothing but yield every result in block 157 # these method returns nothing but yield every result in block
107 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}, &block) 158 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}, &block)
108 logger.debug "<cvs> revisions path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}" 159 path_with_project_utf8 = path_with_proj(path)
109 160 path_with_project_locale = scm_iconv(@path_encoding, 'UTF-8', path_with_project_utf8)
110 path_with_project="#{url}#{with_leading_slash(path)}" 161 logger.debug "<cvs> revisions path:" +
111 cmd = "#{CVS_BIN} -d #{root_url} rlog" 162 "'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
112 cmd << " -d\">#{time_to_cvstime(identifier_from)}\"" if identifier_from 163 cmd_args = %w|-q rlog|
113 cmd << " #{shell_quote path_with_project}" 164 cmd_args << "-d" << ">#{time_to_cvstime_rlog(identifier_from)}" if identifier_from
114 shellout(cmd) do |io| 165 cmd_args << path_with_project_utf8
115 state="entry_start" 166 scm_cmd(*cmd_args) do |io|
116 167 state = "entry_start"
117 commit_log=String.new 168 commit_log = String.new
118 revision=nil 169 revision = nil
119 date=nil 170 date = nil
120 author=nil 171 author = nil
121 entry_path=nil 172 entry_path = nil
122 entry_name=nil 173 entry_name = nil
123 file_state=nil 174 file_state = nil
124 branch_map=nil 175 branch_map = nil
125 176 io.each_line() do |line|
126 io.each_line() do |line| 177 if state != "revision" && /^#{ENDLOG}/ =~ line
127 178 commit_log = String.new
128 if state!="revision" && /^#{ENDLOG}/ =~ line 179 revision = nil
129 commit_log=String.new 180 state = "entry_start"
130 revision=nil
131 state="entry_start"
132 end 181 end
133 182 if state == "entry_start"
134 if state=="entry_start" 183 branch_map = Hash.new
135 branch_map=Hash.new 184 if /^RCS file: #{Regexp.escape(root_url_path)}\/#{Regexp.escape(path_with_project_locale)}(.+),v$/ =~ line
136 if /^RCS file: #{Regexp.escape(root_url_path)}\/#{Regexp.escape(path_with_project)}(.+),v$/ =~ line
137 entry_path = normalize_cvs_path($1) 185 entry_path = normalize_cvs_path($1)
138 entry_name = normalize_path(File.basename($1)) 186 entry_name = normalize_path(File.basename($1))
139 logger.debug("Path #{entry_path} <=> Name #{entry_name}") 187 logger.debug("Path #{entry_path} <=> Name #{entry_name}")
140 elsif /^head: (.+)$/ =~ line 188 elsif /^head: (.+)$/ =~ line
141 entry_headRev = $1 #unless entry.nil? 189 entry_headRev = $1 #unless entry.nil?
142 elsif /^symbolic names:/ =~ line 190 elsif /^symbolic names:/ =~ line
143 state="symbolic" #unless entry.nil? 191 state = "symbolic" #unless entry.nil?
144 elsif /^#{STARTLOG}/ =~ line 192 elsif /^#{STARTLOG}/ =~ line
145 commit_log=String.new 193 commit_log = String.new
146 state="revision" 194 state = "revision"
147 end 195 end
148 next 196 next
149 elsif state=="symbolic" 197 elsif state == "symbolic"
150 if /^(.*):\s(.*)/ =~ (line.strip) 198 if /^(.*):\s(.*)/ =~ (line.strip)
151 branch_map[$1]=$2 199 branch_map[$1] = $2
152 else 200 else
153 state="tags" 201 state = "tags"
154 next 202 next
155 end 203 end
156 elsif state=="tags" 204 elsif state == "tags"
157 if /^#{STARTLOG}/ =~ line 205 if /^#{STARTLOG}/ =~ line
158 commit_log = "" 206 commit_log = ""
159 state="revision" 207 state = "revision"
160 elsif /^#{ENDLOG}/ =~ line 208 elsif /^#{ENDLOG}/ =~ line
161 state="head" 209 state = "head"
162 end 210 end
163 next 211 next
164 elsif state=="revision" 212 elsif state == "revision"
165 if /^#{ENDLOG}/ =~ line || /^#{STARTLOG}/ =~ line 213 if /^#{ENDLOG}/ =~ line || /^#{STARTLOG}/ =~ line
166 if revision 214 if revision
167 215 revHelper = CvsRevisionHelper.new(revision)
168 revHelper=CvsRevisionHelper.new(revision) 216 revBranch = "HEAD"
169 revBranch="HEAD" 217 branch_map.each() do |branch_name, branch_point|
170
171 branch_map.each() do |branch_name,branch_point|
172 if revHelper.is_in_branch_with_symbol(branch_point) 218 if revHelper.is_in_branch_with_symbol(branch_point)
173 revBranch=branch_name 219 revBranch = branch_name
174 end 220 end
175 end 221 end
176
177 logger.debug("********** YIELD Revision #{revision}::#{revBranch}") 222 logger.debug("********** YIELD Revision #{revision}::#{revBranch}")
178 223 yield Revision.new({
179 yield Revision.new({ 224 :time => date,
180 :time => date, 225 :author => author,
181 :author => author, 226 :message => commit_log.chomp,
182 :message=>commit_log.chomp,
183 :paths => [{ 227 :paths => [{
184 :revision => revision, 228 :revision => revision,
185 :branch=> revBranch, 229 :branch => revBranch,
186 :path=>entry_path, 230 :path => scm_iconv('UTF-8', @path_encoding, entry_path),
187 :name=>entry_name, 231 :name => scm_iconv('UTF-8', @path_encoding, entry_name),
188 :kind=>'file', 232 :kind => 'file',
189 :action=>file_state 233 :action => file_state
190 }] 234 }]
191 }) 235 })
192 end 236 end
193 237 commit_log = String.new
194 commit_log=String.new 238 revision = nil
195 revision=nil
196
197 if /^#{ENDLOG}/ =~ line 239 if /^#{ENDLOG}/ =~ line
198 state="entry_start" 240 state = "entry_start"
199 end 241 end
200 next 242 next
201 end 243 end
202 244
203 if /^branches: (.+)$/ =~ line 245 if /^branches: (.+)$/ =~ line
204 #TODO: version.branch = $1 246 # TODO: version.branch = $1
205 elsif /^revision (\d+(?:\.\d+)+).*$/ =~ line 247 elsif /^revision (\d+(?:\.\d+)+).*$/ =~ line
206 revision = $1 248 revision = $1
207 elsif /^date:\s+(\d+.\d+.\d+\s+\d+:\d+:\d+)/ =~ line 249 elsif /^date:\s+(\d+.\d+.\d+\s+\d+:\d+:\d+)/ =~ line
208 date = Time.parse($1) 250 date = Time.parse($1)
209 author = /author: ([^;]+)/.match(line)[1] 251 line_utf8 = scm_iconv('UTF-8', options[:log_encoding], line)
210 file_state = /state: ([^;]+)/.match(line)[1] 252 author_utf8 = /author: ([^;]+)/.match(line_utf8)[1]
211 #TODO: linechanges only available in CVS.... maybe a feature our SVN implementation. i'm sure, they are 253 author = scm_iconv(options[:log_encoding], 'UTF-8', author_utf8)
212 # useful for stats or something else 254 file_state = /state: ([^;]+)/.match(line)[1]
255 # TODO:
256 # linechanges only available in CVS....
257 # maybe a feature our SVN implementation.
258 # I'm sure, they are useful for stats or something else
213 # linechanges =/lines: \+(\d+) -(\d+)/.match(line) 259 # linechanges =/lines: \+(\d+) -(\d+)/.match(line)
214 # unless linechanges.nil? 260 # unless linechanges.nil?
215 # version.line_plus = linechanges[1] 261 # version.line_plus = linechanges[1]
216 # version.line_minus = linechanges[2] 262 # version.line_minus = linechanges[2]
217 # else 263 # else
218 # version.line_plus = 0 264 # version.line_plus = 0
219 # version.line_minus = 0 265 # version.line_minus = 0
220 # end 266 # end
221 else 267 else
222 commit_log << line unless line =~ /^\*\*\* empty log message \*\*\*/ 268 commit_log << line unless line =~ /^\*\*\* empty log message \*\*\*/
223 end 269 end
224 end 270 end
225 end 271 end
226 end 272 end
227 end 273 rescue ScmCommandAborted
228 274 Revisions.new
275 end
276
229 def diff(path, identifier_from, identifier_to=nil) 277 def diff(path, identifier_from, identifier_to=nil)
230 logger.debug "<cvs> diff path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}" 278 logger.debug "<cvs> diff path:'#{path}'" +
231 path_with_project="#{url}#{with_leading_slash(path)}" 279 ",identifier_from #{identifier_from}, identifier_to #{identifier_to}"
232 cmd = "#{CVS_BIN} -d #{root_url} rdiff -u -r#{identifier_to} -r#{identifier_from} #{shell_quote path_with_project}" 280 cmd_args = %w|rdiff -u|
281 cmd_args << "-r#{identifier_to}"
282 cmd_args << "-r#{identifier_from}"
283 cmd_args << path_with_proj(path)
233 diff = [] 284 diff = []
234 shellout(cmd) do |io| 285 scm_cmd(*cmd_args) do |io|
235 io.each_line do |line| 286 io.each_line do |line|
236 diff << line 287 diff << line
237 end 288 end
238 end 289 end
239 return nil if $? && $?.exitstatus != 0
240 diff 290 diff
241 end 291 rescue ScmCommandAborted
242 292 nil
293 end
294
243 def cat(path, identifier=nil) 295 def cat(path, identifier=nil)
244 identifier = (identifier) ? identifier : "HEAD" 296 identifier = (identifier) ? identifier : "HEAD"
245 logger.debug "<cvs> cat path:'#{path}',identifier #{identifier}" 297 logger.debug "<cvs> cat path:'#{path}',identifier #{identifier}"
246 path_with_project="#{url}#{with_leading_slash(path)}" 298 cmd_args = %w|-q co|
247 cmd = "#{CVS_BIN} -d #{root_url} co" 299 cmd_args << "-D" << time_to_cvstime(identifier) if identifier
248 cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier 300 cmd_args << "-p" << path_with_proj(path)
249 cmd << " -p #{shell_quote path_with_project}"
250 cat = nil 301 cat = nil
251 shellout(cmd) do |io| 302 scm_cmd(*cmd_args) do |io|
303 io.binmode
252 cat = io.read 304 cat = io.read
253 end 305 end
254 return nil if $? && $?.exitstatus != 0
255 cat 306 cat
256 end 307 rescue ScmCommandAborted
308 nil
309 end
257 310
258 def annotate(path, identifier=nil) 311 def annotate(path, identifier=nil)
259 identifier = (identifier) ? identifier : "HEAD" 312 identifier = (identifier) ? identifier : "HEAD"
260 logger.debug "<cvs> annotate path:'#{path}',identifier #{identifier}" 313 logger.debug "<cvs> annotate path:'#{path}',identifier #{identifier}"
261 path_with_project="#{url}#{with_leading_slash(path)}" 314 cmd_args = %w|rannotate|
262 cmd = "#{CVS_BIN} -d #{root_url} rannotate -r#{identifier} #{shell_quote path_with_project}" 315 cmd_args << "-D" << time_to_cvstime(identifier) if identifier
316 cmd_args << path_with_proj(path)
263 blame = Annotate.new 317 blame = Annotate.new
264 shellout(cmd) do |io| 318 scm_cmd(*cmd_args) do |io|
265 io.each_line do |line| 319 io.each_line do |line|
266 next unless line =~ %r{^([\d\.]+)\s+\(([^\)]+)\s+[^\)]+\):\s(.*)$} 320 next unless line =~ %r{^([\d\.]+)\s+\(([^\)]+)\s+[^\)]+\):\s(.*)$}
267 blame.add_line($3.rstrip, Revision.new(:revision => $1, :author => $2.strip)) 321 blame.add_line(
268 end 322 $3.rstrip,
269 end 323 Revision.new(
270 return nil if $? && $?.exitstatus != 0 324 :revision => $1,
325 :identifier => nil,
326 :author => $2.strip
327 ))
328 end
329 end
271 blame 330 blame
272 end 331 rescue ScmCommandAborted
273 332 Annotate.new
333 end
334
274 private 335 private
275 336
276 # Returns the root url without the connexion string 337 # Returns the root url without the connexion string
277 # :pserver:anonymous@foo.bar:/path => /path 338 # :pserver:anonymous@foo.bar:/path => /path
278 # :ext:cvsservername:/path => /path 339 # :ext:cvsservername:/path => /path
279 def root_url_path 340 def root_url_path
280 root_url.to_s.gsub(/^:.+:\d*/, '') 341 root_url.to_s.gsub(/^:.+:\d*/, '')
281 end 342 end
282 343
283 # convert a date/time into the CVS-format 344 # convert a date/time into the CVS-format
284 def time_to_cvstime(time) 345 def time_to_cvstime(time)
285 return nil if time.nil? 346 return nil if time.nil?
347 time = Time.now if time == 'HEAD'
348
286 unless time.kind_of? Time 349 unless time.kind_of? Time
287 time = Time.parse(time) 350 time = Time.parse(time)
288 end 351 end
289 return time.strftime("%Y-%m-%d %H:%M:%S") 352 return time_to_cvstime_rlog(time)
290 end 353 end
291 354
355 def time_to_cvstime_rlog(time)
356 return nil if time.nil?
357 t1 = time.clone.localtime
358 return t1.strftime("%Y-%m-%d %H:%M:%S")
359 end
360
292 def normalize_cvs_path(path) 361 def normalize_cvs_path(path)
293 normalize_path(path.gsub(/Attic\//,'')) 362 normalize_path(path.gsub(/Attic\//,''))
294 end 363 end
295 364
296 def normalize_path(path) 365 def normalize_path(path)
297 path.sub(/^(\/)*(.*)/,'\2').sub(/(.*)(,v)+/,'\1') 366 path.sub(/^(\/)*(.*)/,'\2').sub(/(.*)(,v)+/,'\1')
298 end 367 end
299 end 368
300 369 def path_with_proj(path)
370 "#{url}#{with_leading_slash(path)}"
371 end
372 private :path_with_proj
373
374 class Revision < Redmine::Scm::Adapters::Revision
375 # Returns the readable identifier
376 def format_identifier
377 revision.to_s
378 end
379 end
380
381 def scm_cmd(*args, &block)
382 full_args = [CVS_BIN, '-d', root_url]
383 full_args += args
384 full_args_locale = []
385 full_args.map do |e|
386 full_args_locale << scm_iconv(@path_encoding, 'UTF-8', e)
387 end
388 ret = shellout(full_args_locale.map { |e| shell_quote e.to_s }.join(' '), &block)
389 if $? && $?.exitstatus != 0
390 raise ScmCommandAborted, "cvs exited with non-zero status: #{$?.exitstatus}"
391 end
392 ret
393 end
394 private :scm_cmd
395 end
396
301 class CvsRevisionHelper 397 class CvsRevisionHelper
302 attr_accessor :complete_rev, :revision, :base, :branchid 398 attr_accessor :complete_rev, :revision, :base, :branchid
303 399
304 def initialize(complete_rev) 400 def initialize(complete_rev)
305 @complete_rev = complete_rev 401 @complete_rev = complete_rev
306 parseRevision() 402 parseRevision()
307 end 403 end
308 404
309 def branchPoint 405 def branchPoint
310 return @base 406 return @base
311 end 407 end
312 408
313 def branchVersion 409 def branchVersion
314 if isBranchRevision 410 if isBranchRevision
315 return @base+"."+@branchid 411 return @base+"."+@branchid
316 end 412 end
317 return @base 413 return @base
318 end 414 end
319 415
320 def isBranchRevision 416 def isBranchRevision
321 !@branchid.nil? 417 !@branchid.nil?
322 end 418 end
323 419
324 def prevRev 420 def prevRev
325 unless @revision==0 421 unless @revision == 0
326 return buildRevision(@revision-1) 422 return buildRevision( @revision - 1 )
327 end 423 end
328 return buildRevision(@revision) 424 return buildRevision( @revision )
329 end 425 end
330 426
331 def is_in_branch_with_symbol(branch_symbol) 427 def is_in_branch_with_symbol(branch_symbol)
332 bpieces=branch_symbol.split(".") 428 bpieces = branch_symbol.split(".")
333 branch_start="#{bpieces[0..-3].join(".")}.#{bpieces[-1]}" 429 branch_start = "#{bpieces[0..-3].join(".")}.#{bpieces[-1]}"
334 return (branchVersion==branch_start) 430 return ( branchVersion == branch_start )
335 end 431 end
336 432
337 private 433 private
338 def buildRevision(rev) 434 def buildRevision(rev)
339 if rev== 0 435 if rev == 0
340 @base 436 if @branchid.nil?
341 elsif @branchid.nil? 437 @base + ".0"
342 @base+"."+rev.to_s 438 else
439 @base
440 end
441 elsif @branchid.nil?
442 @base + "." + rev.to_s
343 else 443 else
344 @base+"."+@branchid+"."+rev.to_s 444 @base + "." + @branchid + "." + rev.to_s
345 end 445 end
346 end 446 end
347 447
348 # Interpretiert die cvs revisionsnummern wie z.b. 1.14 oder 1.3.0.15 448 # Interpretiert die cvs revisionsnummern wie z.b. 1.14 oder 1.3.0.15
349 def parseRevision() 449 def parseRevision()
350 pieces=@complete_rev.split(".") 450 pieces = @complete_rev.split(".")
351 @revision=pieces.last.to_i 451 @revision = pieces.last.to_i
352 baseSize=1 452 baseSize = 1
353 baseSize+=(pieces.size/2) 453 baseSize += (pieces.size / 2)
354 @base=pieces[0..-baseSize].join(".") 454 @base = pieces[0..-baseSize].join(".")
355 if baseSize > 2 455 if baseSize > 2
356 @branchid=pieces[-2] 456 @branchid = pieces[-2]
357 end 457 end
358 end 458 end
359 end 459 end
360 end 460 end
361 end 461 end
362 end 462 end