| 
									
										
										
										
											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 'cgi' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module Redmine | 
					
						
							|  |  |  |   module Scm | 
					
						
							|  |  |  |     module Adapters     | 
					
						
							|  |  |  |       class CommandFailed < StandardError #:nodoc: | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       class AbstractAdapter #:nodoc: | 
					
						
							|  |  |  |         def initialize(url, root_url=nil, login=nil, password=nil) | 
					
						
							|  |  |  |           @url = url | 
					
						
							|  |  |  |           @login = login if login && !login.empty? | 
					
						
							|  |  |  |           @password = (password || "") if @login | 
					
						
							|  |  |  |           @root_url = root_url.blank? ? retrieve_root_url : root_url | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         def adapter_name | 
					
						
							|  |  |  |           'Abstract' | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |          | 
					
						
							| 
									
										
										
										
											2007-06-24 19:30:38 +00:00
										 |  |  |         def supports_cat? | 
					
						
							|  |  |  |           true | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2007-12-02 20:58:02 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         def supports_annotate? | 
					
						
							|  |  |  |           respond_to?('annotate') | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2007-06-24 19:30:38 +00:00
										 |  |  |          | 
					
						
							| 
									
										
										
										
											2007-06-12 20:12:05 +00:00
										 |  |  |         def root_url | 
					
						
							|  |  |  |           @root_url | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |         def url | 
					
						
							|  |  |  |           @url | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |         # get info about the svn repository | 
					
						
							|  |  |  |         def info | 
					
						
							|  |  |  |           return nil | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         # Returns the entry identified by path and revision identifier | 
					
						
							|  |  |  |         # or nil if entry doesn't exist in the repository | 
					
						
							|  |  |  |         def entry(path=nil, identifier=nil) | 
					
						
							|  |  |  |           e = entries(path, identifier) | 
					
						
							|  |  |  |           e ? e.first : nil | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         # Returns an Entries collection | 
					
						
							|  |  |  |         # or nil if the given path doesn't exist in the repository | 
					
						
							|  |  |  |         def entries(path=nil, identifier=nil) | 
					
						
							|  |  |  |           return nil | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |         def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}) | 
					
						
							|  |  |  |           return nil | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         def diff(path, identifier_from, identifier_to=nil, type="inline") | 
					
						
							|  |  |  |           return nil | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         def cat(path, identifier=nil) | 
					
						
							|  |  |  |           return nil | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2007-12-02 20:58:02 +00:00
										 |  |  |          | 
					
						
							| 
									
										
										
										
											2007-06-12 20:12:05 +00:00
										 |  |  |         def with_leading_slash(path) | 
					
						
							|  |  |  |           path ||= '' | 
					
						
							|  |  |  |           (path[0,1]!="/") ? "/#{path}" : path | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2007-10-19 17:59:59 +00:00
										 |  |  |          | 
					
						
							|  |  |  |         def shell_quote(str) | 
					
						
							|  |  |  |           if RUBY_PLATFORM =~ /mswin/ | 
					
						
							|  |  |  |             '"' + str.gsub(/"/, '\\"') + '"' | 
					
						
							|  |  |  |           else | 
					
						
							|  |  |  |             "'" + str.gsub(/'/, "'\"'\"'") + "'" | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2007-06-12 20:12:05 +00:00
										 |  |  |                | 
					
						
							|  |  |  |       private | 
					
						
							|  |  |  |         def retrieve_root_url | 
					
						
							|  |  |  |           info = self.info | 
					
						
							|  |  |  |           info ? info.root_url : nil | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         def target(path) | 
					
						
							| 
									
										
										
										
											2008-03-12 23:00:11 +00:00
										 |  |  |           path ||= '' | 
					
						
							|  |  |  |           base = path.match(/^\//) ? root_url : url | 
					
						
							|  |  |  |           shell_quote("#{base}/#{path}".gsub(/[?<>\*]/, '')) | 
					
						
							| 
									
										
										
										
											2007-06-12 20:12:05 +00:00
										 |  |  |         end | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |         def logger | 
					
						
							|  |  |  |           RAILS_DEFAULT_LOGGER | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |         def shellout(cmd, &block) | 
					
						
							|  |  |  |           logger.debug "Shelling out: #{cmd}" if logger && logger.debug? | 
					
						
							| 
									
										
										
										
											2007-12-15 12:14:40 +00:00
										 |  |  |           begin | 
					
						
							|  |  |  |             IO.popen(cmd, "r+") do |io| | 
					
						
							|  |  |  |               io.close_write | 
					
						
							|  |  |  |               block.call(io) if block_given? | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |           rescue Errno::ENOENT => e | 
					
						
							|  |  |  |             # The command failed, log it and re-raise | 
					
						
							| 
									
										
										
										
											2008-01-03 18:28:14 +00:00
										 |  |  |             logger.error("SCM command failed: #{cmd}\n  with: #{e.message}") | 
					
						
							| 
									
										
										
										
											2008-01-23 17:25:11 +00:00
										 |  |  |             raise CommandFailed.new(e.message) | 
					
						
							| 
									
										
										
										
											2007-06-12 20:12:05 +00:00
										 |  |  |           end | 
					
						
							|  |  |  |         end   | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       class Entries < Array | 
					
						
							|  |  |  |         def sort_by_name | 
					
						
							|  |  |  |           sort {|x,y|  | 
					
						
							|  |  |  |             if x.kind == y.kind | 
					
						
							|  |  |  |               x.name <=> y.name | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |               x.kind <=> y.kind | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |           }    | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         def revisions | 
					
						
							|  |  |  |           revisions ||= Revisions.new(collect{|entry| entry.lastrev}.compact) | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       class Info | 
					
						
							|  |  |  |         attr_accessor :root_url, :lastrev | 
					
						
							|  |  |  |         def initialize(attributes={}) | 
					
						
							|  |  |  |           self.root_url = attributes[:root_url] if attributes[:root_url] | 
					
						
							|  |  |  |           self.lastrev = attributes[:lastrev] | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       class Entry | 
					
						
							|  |  |  |         attr_accessor :name, :path, :kind, :size, :lastrev | 
					
						
							|  |  |  |         def initialize(attributes={}) | 
					
						
							|  |  |  |           self.name = attributes[:name] if attributes[:name] | 
					
						
							|  |  |  |           self.path = attributes[:path] if attributes[:path] | 
					
						
							|  |  |  |           self.kind = attributes[:kind] if attributes[:kind] | 
					
						
							|  |  |  |           self.size = attributes[:size].to_i if attributes[:size] | 
					
						
							|  |  |  |           self.lastrev = attributes[:lastrev] | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         def is_file? | 
					
						
							|  |  |  |           'file' == self.kind | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         def is_dir? | 
					
						
							|  |  |  |           'dir' == self.kind | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         def is_text? | 
					
						
							|  |  |  |           Redmine::MimeType.is_type?('text', name) | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       class Revisions < Array | 
					
						
							|  |  |  |         def latest | 
					
						
							|  |  |  |           sort {|x,y| | 
					
						
							|  |  |  |             unless x.time.nil? or y.time.nil? | 
					
						
							|  |  |  |               x.time <=> y.time | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |               0
 | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |           }.last | 
					
						
							|  |  |  |         end  | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       class Revision | 
					
						
							|  |  |  |         attr_accessor :identifier, :scmid, :name, :author, :time, :message, :paths, :revision, :branch | 
					
						
							|  |  |  |         def initialize(attributes={}) | 
					
						
							|  |  |  |           self.identifier = attributes[:identifier] | 
					
						
							|  |  |  |           self.scmid = attributes[:scmid] | 
					
						
							|  |  |  |           self.name = attributes[:name] || self.identifier | 
					
						
							|  |  |  |           self.author = attributes[:author] | 
					
						
							|  |  |  |           self.time = attributes[:time] | 
					
						
							|  |  |  |           self.message = attributes[:message] || "" | 
					
						
							|  |  |  |           self.paths = attributes[:paths] | 
					
						
							|  |  |  |           self.revision = attributes[:revision] | 
					
						
							|  |  |  |           self.branch = attributes[:branch] | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |       # A line of Diff | 
					
						
							|  |  |  |       class Diff   | 
					
						
							|  |  |  |         attr_accessor :nb_line_left | 
					
						
							|  |  |  |         attr_accessor :line_left | 
					
						
							|  |  |  |         attr_accessor :nb_line_right | 
					
						
							|  |  |  |         attr_accessor :line_right | 
					
						
							|  |  |  |         attr_accessor :type_diff_right | 
					
						
							|  |  |  |         attr_accessor :type_diff_left | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         def initialize () | 
					
						
							|  |  |  |           self.nb_line_left = '' | 
					
						
							|  |  |  |           self.nb_line_right = '' | 
					
						
							|  |  |  |           self.line_left = '' | 
					
						
							|  |  |  |           self.line_right = '' | 
					
						
							|  |  |  |           self.type_diff_right = '' | 
					
						
							|  |  |  |           self.type_diff_left = '' | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |         def inspect | 
					
						
							|  |  |  |           puts '### Start Line Diff ###' | 
					
						
							|  |  |  |           puts self.nb_line_left | 
					
						
							|  |  |  |           puts self.line_left | 
					
						
							|  |  |  |           puts self.nb_line_right | 
					
						
							|  |  |  |           puts self.line_right | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |       class DiffTableList < Array   | 
					
						
							|  |  |  |         def initialize (diff, type="inline") | 
					
						
							|  |  |  |             diff_table = DiffTable.new type | 
					
						
							|  |  |  |             diff.each do |line| | 
					
						
							| 
									
										
										
										
											2007-06-24 19:30:38 +00:00
										 |  |  |                 if line =~ /^(---|\+\+\+) (.*)$/ | 
					
						
							| 
									
										
										
										
											2007-06-12 20:12:05 +00:00
										 |  |  |                     self << diff_table if diff_table.length > 1
 | 
					
						
							|  |  |  |                     diff_table = DiffTable.new type | 
					
						
							|  |  |  |                 end | 
					
						
							|  |  |  |                 a = diff_table.add_line line | 
					
						
							|  |  |  |             end | 
					
						
							| 
									
										
										
										
											2007-12-19 20:11:44 +00:00
										 |  |  |             self << diff_table unless diff_table.empty? | 
					
						
							|  |  |  |             self | 
					
						
							| 
									
										
										
										
											2007-06-12 20:12:05 +00:00
										 |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |       # Class for create a Diff | 
					
						
							|  |  |  |       class DiffTable < Hash   | 
					
						
							|  |  |  |         attr_reader :file_name, :line_num_l, :line_num_r     | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |         # Initialize with a Diff file and the type of Diff View | 
					
						
							|  |  |  |         # The type view must be inline or sbs (side_by_side) | 
					
						
							| 
									
										
										
										
											2007-12-02 20:58:02 +00:00
										 |  |  |         def initialize(type="inline") | 
					
						
							| 
									
										
										
										
											2007-06-12 20:12:05 +00:00
										 |  |  |           @parsing = false | 
					
						
							|  |  |  |           @nb_line = 1
 | 
					
						
							|  |  |  |           @start = false | 
					
						
							|  |  |  |           @before = 'same' | 
					
						
							|  |  |  |           @second = true | 
					
						
							|  |  |  |           @type = type | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |         # Function for add a line of this Diff | 
					
						
							|  |  |  |         def add_line(line) | 
					
						
							|  |  |  |           unless @parsing | 
					
						
							| 
									
										
										
										
											2007-06-24 19:30:38 +00:00
										 |  |  |             if line =~ /^(---|\+\+\+) (.*)$/ | 
					
						
							| 
									
										
										
										
											2007-06-12 20:12:05 +00:00
										 |  |  |               @file_name = $2 | 
					
						
							|  |  |  |               return false | 
					
						
							|  |  |  |             elsif line =~ /^@@ (\+|\-)(\d+)(,\d+)? (\+|\-)(\d+)(,\d+)? @@/ | 
					
						
							|  |  |  |               @line_num_l = $5.to_i | 
					
						
							|  |  |  |               @line_num_r = $2.to_i | 
					
						
							|  |  |  |               @parsing = true | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |           else | 
					
						
							|  |  |  |             if line =~ /^[^\+\-\s@\\]/ | 
					
						
							|  |  |  |               self.delete(self.keys.sort.last) | 
					
						
							|  |  |  |               @parsing = false | 
					
						
							|  |  |  |               return false | 
					
						
							|  |  |  |             elsif line =~ /^@@ (\+|\-)(\d+)(,\d+)? (\+|\-)(\d+)(,\d+)? @@/ | 
					
						
							|  |  |  |               @line_num_l = $5.to_i | 
					
						
							|  |  |  |               @line_num_r = $2.to_i | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |               @nb_line += 1 if parse_line(line, @type)           | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |           return true | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |         def inspect | 
					
						
							|  |  |  |           puts '### DIFF TABLE ###' | 
					
						
							|  |  |  |           puts "file : #{file_name}" | 
					
						
							|  |  |  |           self.each do |d| | 
					
						
							|  |  |  |             d.inspect | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |       private   | 
					
						
							|  |  |  |         # Test if is a Side By Side type | 
					
						
							|  |  |  |         def sbs?(type, func) | 
					
						
							|  |  |  |           if @start and type == "sbs" | 
					
						
							|  |  |  |             if @before == func and @second | 
					
						
							|  |  |  |               tmp_nb_line = @nb_line | 
					
						
							|  |  |  |               self[tmp_nb_line] = Diff.new | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |                 @second = false | 
					
						
							|  |  |  |                 tmp_nb_line = @start | 
					
						
							|  |  |  |                 @start += 1
 | 
					
						
							|  |  |  |                 @nb_line -= 1
 | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |           else | 
					
						
							|  |  |  |             tmp_nb_line = @nb_line | 
					
						
							|  |  |  |             @start = @nb_line | 
					
						
							|  |  |  |             self[tmp_nb_line] = Diff.new | 
					
						
							|  |  |  |             @second = true | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |           unless self[tmp_nb_line] | 
					
						
							|  |  |  |             @nb_line += 1
 | 
					
						
							|  |  |  |             self[tmp_nb_line] = Diff.new | 
					
						
							|  |  |  |           else | 
					
						
							|  |  |  |             self[tmp_nb_line] | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |         # Escape the HTML for the diff | 
					
						
							|  |  |  |         def escapeHTML(line) | 
					
						
							| 
									
										
										
										
											2007-08-15 20:20:18 +00:00
										 |  |  |             CGI.escapeHTML(line) | 
					
						
							| 
									
										
										
										
											2007-06-12 20:12:05 +00:00
										 |  |  |         end | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2007-12-02 20:58:02 +00:00
										 |  |  |         def parse_line(line, type="inline") | 
					
						
							| 
									
										
										
										
											2007-06-12 20:12:05 +00:00
										 |  |  |           if line[0, 1] == "+" | 
					
						
							|  |  |  |             diff = sbs? type, 'add' | 
					
						
							|  |  |  |             @before = 'add' | 
					
						
							|  |  |  |             diff.line_left = escapeHTML line[1..-1] | 
					
						
							|  |  |  |             diff.nb_line_left = @line_num_l | 
					
						
							|  |  |  |             diff.type_diff_left = 'diff_in' | 
					
						
							|  |  |  |             @line_num_l += 1
 | 
					
						
							|  |  |  |             true | 
					
						
							|  |  |  |           elsif line[0, 1] == "-" | 
					
						
							|  |  |  |             diff = sbs? type, 'remove' | 
					
						
							|  |  |  |             @before = 'remove' | 
					
						
							|  |  |  |             diff.line_right = escapeHTML line[1..-1] | 
					
						
							|  |  |  |             diff.nb_line_right = @line_num_r | 
					
						
							|  |  |  |             diff.type_diff_right = 'diff_out' | 
					
						
							|  |  |  |             @line_num_r += 1
 | 
					
						
							|  |  |  |             true | 
					
						
							|  |  |  |           elsif line[0, 1] =~ /\s/ | 
					
						
							|  |  |  |             @before = 'same' | 
					
						
							|  |  |  |             @start = false | 
					
						
							|  |  |  |             diff = Diff.new | 
					
						
							|  |  |  |             diff.line_right = escapeHTML line[1..-1] | 
					
						
							|  |  |  |             diff.nb_line_right = @line_num_r | 
					
						
							|  |  |  |             diff.line_left = escapeHTML line[1..-1] | 
					
						
							|  |  |  |             diff.nb_line_left = @line_num_l | 
					
						
							|  |  |  |             self[@nb_line] = diff | 
					
						
							|  |  |  |             @line_num_l += 1
 | 
					
						
							|  |  |  |             @line_num_r += 1
 | 
					
						
							|  |  |  |             true | 
					
						
							|  |  |  |           elsif line[0, 1] = "\\" | 
					
						
							|  |  |  |             true | 
					
						
							|  |  |  |           else | 
					
						
							|  |  |  |             false | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2007-12-02 20:58:02 +00:00
										 |  |  |      | 
					
						
							|  |  |  |       class Annotate | 
					
						
							|  |  |  |         attr_reader :lines, :revisions | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         def initialize | 
					
						
							|  |  |  |           @lines = [] | 
					
						
							|  |  |  |           @revisions = [] | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         def add_line(line, revision) | 
					
						
							|  |  |  |           @lines << line | 
					
						
							|  |  |  |           @revisions << revision | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         def content | 
					
						
							|  |  |  |           content = lines.join("\n") | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         def empty? | 
					
						
							|  |  |  |           lines.empty? | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2007-06-12 20:12:05 +00:00
										 |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | end |