| 
									
										
										
										
											2007-06-12 20:12:05 +00:00
										 |  |  | # redMine - project management software | 
					
						
							|  |  |  | # Copyright (C) 2006-2007  Jean-Philippe Lang | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # This program is free software; you can redistribute it and/or | 
					
						
							|  |  |  | # modify it under the terms of the GNU General Public License | 
					
						
							|  |  |  | # as published by the Free Software Foundation; either version 2 | 
					
						
							|  |  |  | # of the License, or (at your option) any later version. | 
					
						
							|  |  |  | #  | 
					
						
							|  |  |  | # This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  | # GNU General Public License for more details. | 
					
						
							|  |  |  | #  | 
					
						
							|  |  |  | # You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  | # along with this program; if not, write to the Free Software | 
					
						
							|  |  |  | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | require 'redmine/scm/adapters/abstract_adapter' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module Redmine | 
					
						
							|  |  |  |   module Scm | 
					
						
							|  |  |  |     module Adapters | 
					
						
							|  |  |  |       class CvsAdapter < AbstractAdapter | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # CVS executable name | 
					
						
							|  |  |  |         CVS_BIN = "cvs" | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |         # Guidelines for the input: | 
					
						
							|  |  |  |         #  url -> the project-path, relative to the cvsroot (eg. module name) | 
					
						
							|  |  |  |         #  root_url -> the good old, sometimes damned, CVSROOT | 
					
						
							|  |  |  |         #  login -> unnecessary | 
					
						
							|  |  |  |         #  password -> unnecessary too | 
					
						
							|  |  |  |         def initialize(url, root_url=nil, login=nil, password=nil) | 
					
						
							|  |  |  |           @url = url | 
					
						
							|  |  |  |           @login = login if login && !login.empty? | 
					
						
							|  |  |  |           @password = (password || "") if @login | 
					
						
							|  |  |  |           #TODO: better Exception here (IllegalArgumentException) | 
					
						
							|  |  |  |           raise CommandFailed if root_url.blank? | 
					
						
							|  |  |  |           @root_url = root_url | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         def root_url | 
					
						
							|  |  |  |           @root_url | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         def url | 
					
						
							|  |  |  |           @url | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         def info | 
					
						
							|  |  |  |           logger.debug "<cvs> info" | 
					
						
							|  |  |  |           Info.new({:root_url => @root_url, :lastrev => nil}) | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         def get_previous_revision(revision) | 
					
						
							|  |  |  |           CvsRevisionHelper.new(revision).prevRev | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |         # Returns an Entries collection | 
					
						
							|  |  |  |         # or nil if the given path doesn't exist in the repository | 
					
						
							|  |  |  |         # this method is used by the repository-browser (aka LIST) | 
					
						
							|  |  |  |         def entries(path=nil, identifier=nil) | 
					
						
							|  |  |  |           logger.debug "<cvs> entries '#{path}' with identifier '#{identifier}'" | 
					
						
							|  |  |  |           path_with_project="#{url}#{with_leading_slash(path)}" | 
					
						
							|  |  |  |           entries = Entries.new | 
					
						
							| 
									
										
										
										
											2008-12-14 15:57:13 +00:00
										 |  |  |           cmd = "#{CVS_BIN} -d #{root_url} rls -e" | 
					
						
							| 
									
										
										
										
											2008-04-06 10:35:55 +00:00
										 |  |  |           cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier | 
					
						
							| 
									
										
										
										
											2008-06-14 11:42:50 +00:00
										 |  |  |           cmd << " #{shell_quote path_with_project}" | 
					
						
							| 
									
										
										
										
											2007-06-12 20:12:05 +00:00
										 |  |  |           shellout(cmd) do |io| | 
					
						
							|  |  |  |             io.each_line(){|line| | 
					
						
							|  |  |  |               fields=line.chop.split('/',-1) | 
					
						
							|  |  |  |               logger.debug(">>InspectLine #{fields.inspect}") | 
					
						
							|  |  |  |                | 
					
						
							|  |  |  |               if fields[0]!="D" | 
					
						
							|  |  |  |                 entries << Entry.new({:name => fields[-5], | 
					
						
							|  |  |  |                   #:path => fields[-4].include?(path)?fields[-4]:(path + "/"+ fields[-4]), | 
					
						
							|  |  |  |                   :path => "#{path}/#{fields[-5]}", | 
					
						
							|  |  |  |                   :kind => 'file', | 
					
						
							|  |  |  |                   :size => nil, | 
					
						
							|  |  |  |                   :lastrev => Revision.new({ | 
					
						
							|  |  |  |                     :revision => fields[-4], | 
					
						
							|  |  |  |                     :name => fields[-4], | 
					
						
							|  |  |  |                     :time => Time.parse(fields[-3]), | 
					
						
							|  |  |  |                     :author => '' | 
					
						
							|  |  |  |                   }) | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  |               else | 
					
						
							|  |  |  |                 entries << Entry.new({:name => fields[1], | 
					
						
							|  |  |  |                   :path => "#{path}/#{fields[1]}", | 
					
						
							|  |  |  |                   :kind => 'dir', | 
					
						
							|  |  |  |                   :size => nil, | 
					
						
							|  |  |  |                   :lastrev => nil | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  |               end | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |           return nil if $? && $?.exitstatus != 0
 | 
					
						
							|  |  |  |           entries.sort_by_name | 
					
						
							|  |  |  |         end   | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         STARTLOG="----------------------------" | 
					
						
							|  |  |  |         ENDLOG  ="=============================================================================" | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         # Returns all revisions found between identifier_from and identifier_to | 
					
						
							|  |  |  |         # in the repository. both identifier have to be dates or nil. | 
					
						
							|  |  |  |         # these method returns nothing but yield every result in block | 
					
						
							|  |  |  |         def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}, &block) | 
					
						
							|  |  |  |           logger.debug "<cvs> revisions path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}" | 
					
						
							|  |  |  |            | 
					
						
							|  |  |  |           path_with_project="#{url}#{with_leading_slash(path)}" | 
					
						
							|  |  |  |           cmd = "#{CVS_BIN} -d #{root_url} rlog" | 
					
						
							|  |  |  |           cmd << " -d\">#{time_to_cvstime(identifier_from)}\"" if identifier_from | 
					
						
							| 
									
										
										
										
											2008-06-14 11:42:50 +00:00
										 |  |  |           cmd << " #{shell_quote path_with_project}" | 
					
						
							| 
									
										
										
										
											2007-06-12 20:12:05 +00:00
										 |  |  |           shellout(cmd) do |io| | 
					
						
							|  |  |  |             state="entry_start" | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |             commit_log=String.new | 
					
						
							|  |  |  |             revision=nil | 
					
						
							|  |  |  |             date=nil | 
					
						
							|  |  |  |             author=nil | 
					
						
							|  |  |  |             entry_path=nil | 
					
						
							|  |  |  |             entry_name=nil | 
					
						
							|  |  |  |             file_state=nil | 
					
						
							|  |  |  |             branch_map=nil | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |             io.each_line() do |line|             | 
					
						
							|  |  |  |                | 
					
						
							|  |  |  |               if state!="revision" && /^#{ENDLOG}/ =~ line | 
					
						
							|  |  |  |                 commit_log=String.new | 
					
						
							|  |  |  |                 revision=nil | 
					
						
							|  |  |  |                 state="entry_start" | 
					
						
							|  |  |  |               end | 
					
						
							|  |  |  |                | 
					
						
							|  |  |  |               if state=="entry_start" | 
					
						
							|  |  |  |                 branch_map=Hash.new | 
					
						
							| 
									
										
										
										
											2008-12-06 17:40:54 +00:00
										 |  |  |                 if /^RCS file: #{Regexp.escape(root_url_path)}\/#{Regexp.escape(path_with_project)}(.+),v$/ =~ line | 
					
						
							| 
									
										
										
										
											2007-06-12 20:12:05 +00:00
										 |  |  |                   entry_path = normalize_cvs_path($1) | 
					
						
							|  |  |  |                   entry_name = normalize_path(File.basename($1)) | 
					
						
							|  |  |  |                   logger.debug("Path #{entry_path} <=> Name #{entry_name}") | 
					
						
							|  |  |  |                 elsif /^head: (.+)$/ =~ line | 
					
						
							|  |  |  |                   entry_headRev = $1 #unless entry.nil? | 
					
						
							|  |  |  |                 elsif /^symbolic names:/ =~ line | 
					
						
							|  |  |  |                   state="symbolic" #unless entry.nil? | 
					
						
							|  |  |  |                 elsif /^#{STARTLOG}/ =~ line | 
					
						
							|  |  |  |                   commit_log=String.new | 
					
						
							|  |  |  |                   state="revision" | 
					
						
							|  |  |  |                 end   | 
					
						
							|  |  |  |                 next | 
					
						
							|  |  |  |               elsif state=="symbolic" | 
					
						
							|  |  |  |                 if /^(.*):\s(.*)/ =~ (line.strip)  | 
					
						
							|  |  |  |                   branch_map[$1]=$2 | 
					
						
							|  |  |  |                 else | 
					
						
							|  |  |  |                   state="tags" | 
					
						
							|  |  |  |                   next | 
					
						
							|  |  |  |                 end           | 
					
						
							|  |  |  |               elsif state=="tags" | 
					
						
							|  |  |  |                 if /^#{STARTLOG}/ =~ line | 
					
						
							|  |  |  |                   commit_log = "" | 
					
						
							|  |  |  |                   state="revision" | 
					
						
							|  |  |  |                 elsif /^#{ENDLOG}/ =~ line | 
					
						
							|  |  |  |                   state="head" | 
					
						
							|  |  |  |                 end | 
					
						
							|  |  |  |                 next | 
					
						
							|  |  |  |               elsif state=="revision" | 
					
						
							|  |  |  |                 if /^#{ENDLOG}/ =~ line || /^#{STARTLOG}/ =~ line                | 
					
						
							|  |  |  |                   if revision | 
					
						
							|  |  |  |                      | 
					
						
							|  |  |  |                     revHelper=CvsRevisionHelper.new(revision) | 
					
						
							|  |  |  |                     revBranch="HEAD" | 
					
						
							|  |  |  |                      | 
					
						
							|  |  |  |                     branch_map.each() do |branch_name,branch_point| | 
					
						
							|  |  |  |                       if revHelper.is_in_branch_with_symbol(branch_point) | 
					
						
							|  |  |  |                         revBranch=branch_name | 
					
						
							|  |  |  |                       end | 
					
						
							|  |  |  |                     end | 
					
						
							|  |  |  |                      | 
					
						
							|  |  |  |                     logger.debug("********** YIELD Revision #{revision}::#{revBranch}") | 
					
						
							|  |  |  |                      | 
					
						
							|  |  |  |                     yield Revision.new({                     | 
					
						
							|  |  |  |                       :time => date, | 
					
						
							|  |  |  |                       :author => author, | 
					
						
							|  |  |  |                       :message=>commit_log.chomp, | 
					
						
							|  |  |  |                       :paths => [{ | 
					
						
							|  |  |  |                         :revision => revision, | 
					
						
							|  |  |  |                         :branch=> revBranch, | 
					
						
							|  |  |  |                         :path=>entry_path, | 
					
						
							|  |  |  |                         :name=>entry_name, | 
					
						
							|  |  |  |                         :kind=>'file', | 
					
						
							|  |  |  |                         :action=>file_state | 
					
						
							|  |  |  |                       }] | 
					
						
							|  |  |  |                     })                  | 
					
						
							|  |  |  |                   end | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |                   commit_log=String.new | 
					
						
							|  |  |  |                   revision=nil | 
					
						
							|  |  |  |                    | 
					
						
							|  |  |  |                   if /^#{ENDLOG}/ =~ line | 
					
						
							|  |  |  |                     state="entry_start" | 
					
						
							|  |  |  |                   end | 
					
						
							|  |  |  |                   next | 
					
						
							|  |  |  |                 end | 
					
						
							|  |  |  |                    | 
					
						
							|  |  |  |                 if /^branches: (.+)$/ =~ line | 
					
						
							|  |  |  |                   #TODO: version.branch = $1 | 
					
						
							|  |  |  |                 elsif /^revision (\d+(?:\.\d+)+).*$/ =~ line | 
					
						
							|  |  |  |                   revision = $1    | 
					
						
							|  |  |  |                 elsif /^date:\s+(\d+.\d+.\d+\s+\d+:\d+:\d+)/ =~ line | 
					
						
							|  |  |  |                   date      = Time.parse($1) | 
					
						
							|  |  |  |                   author    = /author: ([^;]+)/.match(line)[1] | 
					
						
							|  |  |  |                   file_state     = /state: ([^;]+)/.match(line)[1] | 
					
						
							|  |  |  |                   #TODO: linechanges only available in CVS.... maybe a feature our SVN implementation. i'm sure, they are | 
					
						
							|  |  |  |                   #    useful for stats or something else | 
					
						
							|  |  |  |                   #                linechanges =/lines: \+(\d+) -(\d+)/.match(line) | 
					
						
							|  |  |  |                   #                unless linechanges.nil? | 
					
						
							|  |  |  |                   #                  version.line_plus  = linechanges[1] | 
					
						
							|  |  |  |                   #                  version.line_minus = linechanges[2] | 
					
						
							|  |  |  |                   #                else | 
					
						
							|  |  |  |                   #                  version.line_plus  = 0 | 
					
						
							|  |  |  |                   #                  version.line_minus = 0      | 
					
						
							|  |  |  |                   #                end               | 
					
						
							|  |  |  |                 else             | 
					
						
							|  |  |  |                   commit_log << line unless line =~ /^\*\*\* empty log message \*\*\*/ | 
					
						
							|  |  |  |                 end  | 
					
						
							|  |  |  |               end  | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         end   | 
					
						
							|  |  |  |          | 
					
						
							| 
									
										
										
										
											2008-06-08 16:28:42 +00:00
										 |  |  |         def diff(path, identifier_from, identifier_to=nil) | 
					
						
							| 
									
										
										
										
											2007-06-12 20:12:05 +00:00
										 |  |  |           logger.debug "<cvs> diff path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}" | 
					
						
							|  |  |  |           path_with_project="#{url}#{with_leading_slash(path)}" | 
					
						
							| 
									
										
										
										
											2008-06-14 11:42:50 +00:00
										 |  |  |           cmd = "#{CVS_BIN} -d #{root_url} rdiff -u -r#{identifier_to} -r#{identifier_from} #{shell_quote path_with_project}" | 
					
						
							| 
									
										
										
										
											2007-06-12 20:12:05 +00:00
										 |  |  |           diff = [] | 
					
						
							|  |  |  |           shellout(cmd) do |io| | 
					
						
							|  |  |  |             io.each_line do |line| | 
					
						
							|  |  |  |               diff << line | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |           return nil if $? && $?.exitstatus != 0
 | 
					
						
							| 
									
										
										
										
											2008-06-08 16:28:42 +00:00
										 |  |  |           diff | 
					
						
							| 
									
										
										
										
											2007-06-12 20:12:05 +00:00
										 |  |  |         end   | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         def cat(path, identifier=nil) | 
					
						
							|  |  |  |           identifier = (identifier) ? identifier : "HEAD" | 
					
						
							|  |  |  |           logger.debug "<cvs> cat path:'#{path}',identifier #{identifier}" | 
					
						
							|  |  |  |           path_with_project="#{url}#{with_leading_slash(path)}" | 
					
						
							| 
									
										
										
										
											2008-06-15 15:47:28 +00:00
										 |  |  |           cmd = "#{CVS_BIN} -d #{root_url} co" | 
					
						
							|  |  |  |           cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier | 
					
						
							|  |  |  |           cmd << " -p #{shell_quote path_with_project}" | 
					
						
							| 
									
										
										
										
											2007-06-12 20:12:05 +00:00
										 |  |  |           cat = nil | 
					
						
							|  |  |  |           shellout(cmd) do |io| | 
					
						
							|  |  |  |             cat = io.read | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |           return nil if $? && $?.exitstatus != 0
 | 
					
						
							|  |  |  |           cat | 
					
						
							|  |  |  |         end   | 
					
						
							| 
									
										
										
										
											2007-12-02 20:58:02 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         def annotate(path, identifier=nil) | 
					
						
							|  |  |  |           identifier = (identifier) ? identifier : "HEAD" | 
					
						
							|  |  |  |           logger.debug "<cvs> annotate path:'#{path}',identifier #{identifier}" | 
					
						
							|  |  |  |           path_with_project="#{url}#{with_leading_slash(path)}" | 
					
						
							| 
									
										
										
										
											2008-06-14 11:42:50 +00:00
										 |  |  |           cmd = "#{CVS_BIN} -d #{root_url} rannotate -r#{identifier} #{shell_quote path_with_project}" | 
					
						
							| 
									
										
										
										
											2007-12-02 20:58:02 +00:00
										 |  |  |           blame = Annotate.new | 
					
						
							|  |  |  |           shellout(cmd) do |io| | 
					
						
							|  |  |  |             io.each_line do |line| | 
					
						
							|  |  |  |               next unless line =~ %r{^([\d\.]+)\s+\(([^\)]+)\s+[^\)]+\):\s(.*)$} | 
					
						
							|  |  |  |               blame.add_line($3.rstrip, Revision.new(:revision => $1, :author => $2.strip)) | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |           return nil if $? && $?.exitstatus != 0
 | 
					
						
							|  |  |  |           blame | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |           | 
					
						
							| 
									
										
										
										
											2007-06-12 20:12:05 +00:00
										 |  |  |         private | 
					
						
							| 
									
										
										
										
											2008-12-06 17:40:54 +00:00
										 |  |  |          | 
					
						
							|  |  |  |         # Returns the root url without the connexion string | 
					
						
							|  |  |  |         # :pserver:anonymous@foo.bar:/path => /path | 
					
						
							|  |  |  |         # :ext:cvsservername:/path => /path | 
					
						
							|  |  |  |         def root_url_path | 
					
						
							|  |  |  |           root_url.to_s.gsub(/^:.+:\d*/, '') | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2007-06-12 20:12:05 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # convert a date/time into the CVS-format | 
					
						
							|  |  |  |         def time_to_cvstime(time) | 
					
						
							|  |  |  |           return nil if time.nil? | 
					
						
							|  |  |  |           unless time.kind_of? Time | 
					
						
							|  |  |  |             time = Time.parse(time) | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |           return time.strftime("%Y-%m-%d %H:%M:%S") | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |            | 
					
						
							|  |  |  |         def normalize_cvs_path(path) | 
					
						
							|  |  |  |           normalize_path(path.gsub(/Attic\//,'')) | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |            | 
					
						
							|  |  |  |         def normalize_path(path) | 
					
						
							|  |  |  |           path.sub(/^(\/)*(.*)/,'\2').sub(/(.*)(,v)+/,'\1') | 
					
						
							|  |  |  |         end    | 
					
						
							|  |  |  |       end   | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |       class CvsRevisionHelper | 
					
						
							|  |  |  |         attr_accessor :complete_rev, :revision, :base, :branchid | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         def initialize(complete_rev) | 
					
						
							|  |  |  |           @complete_rev = complete_rev | 
					
						
							|  |  |  |           parseRevision() | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |         def branchPoint | 
					
						
							|  |  |  |           return @base | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |         def branchVersion | 
					
						
							|  |  |  |           if isBranchRevision | 
					
						
							|  |  |  |             return @base+"."+@branchid | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |           return @base | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |         def isBranchRevision | 
					
						
							|  |  |  |           !@branchid.nil? | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         def prevRev | 
					
						
							|  |  |  |           unless @revision==0
 | 
					
						
							|  |  |  |             return buildRevision(@revision-1) | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |           return buildRevision(@revision)     | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         def is_in_branch_with_symbol(branch_symbol) | 
					
						
							|  |  |  |           bpieces=branch_symbol.split(".") | 
					
						
							|  |  |  |           branch_start="#{bpieces[0..-3].join(".")}.#{bpieces[-1]}" | 
					
						
							|  |  |  |           return (branchVersion==branch_start) | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |         private | 
					
						
							|  |  |  |         def buildRevision(rev) | 
					
						
							|  |  |  |           if rev== 0
 | 
					
						
							|  |  |  |             @base | 
					
						
							|  |  |  |           elsif @branchid.nil?  | 
					
						
							|  |  |  |             @base+"."+rev.to_s | 
					
						
							|  |  |  |           else | 
					
						
							|  |  |  |             @base+"."+@branchid+"."+rev.to_s | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         # Interpretiert die cvs revisionsnummern wie z.b. 1.14 oder 1.3.0.15 | 
					
						
							|  |  |  |         def parseRevision() | 
					
						
							|  |  |  |           pieces=@complete_rev.split(".") | 
					
						
							|  |  |  |           @revision=pieces.last.to_i | 
					
						
							|  |  |  |           baseSize=1
 | 
					
						
							|  |  |  |           baseSize+=(pieces.size/2) | 
					
						
							|  |  |  |           @base=pieces[0..-baseSize].join(".") | 
					
						
							|  |  |  |           if baseSize > 2
 | 
					
						
							|  |  |  |             @branchid=pieces[-2] | 
					
						
							|  |  |  |           end      | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | end |