mirror of
				https://github.com/redmine/redmine.git
				synced 2025-11-03 20:06:24 +01:00 
			
		
		
		
	git-svn-id: http://redmine.rubyforge.org/svn/trunk@1034 e93f8b46-1217-0410-a6f0-8f06a7374b81
		
			
				
	
	
		
			387 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			387 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
# 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
 | 
						|
        
 | 
						|
        def supports_cat?
 | 
						|
          true
 | 
						|
        end
 | 
						|
 | 
						|
        def supports_annotate?
 | 
						|
          respond_to?('annotate')
 | 
						|
        end
 | 
						|
        
 | 
						|
        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
 | 
						|
        
 | 
						|
        def with_leading_slash(path)
 | 
						|
          path ||= ''
 | 
						|
          (path[0,1]!="/") ? "/#{path}" : path
 | 
						|
        end
 | 
						|
        
 | 
						|
        def shell_quote(str)
 | 
						|
          if RUBY_PLATFORM =~ /mswin/
 | 
						|
            '"' + str.gsub(/"/, '\\"') + '"'
 | 
						|
          else
 | 
						|
            "'" + str.gsub(/'/, "'\"'\"'") + "'"
 | 
						|
          end
 | 
						|
        end
 | 
						|
              
 | 
						|
      private
 | 
						|
        def retrieve_root_url
 | 
						|
          info = self.info
 | 
						|
          info ? info.root_url : nil
 | 
						|
        end
 | 
						|
        
 | 
						|
        def target(path)
 | 
						|
          path ||= ""
 | 
						|
          base = path.match(/^\//) ? root_url : url    
 | 
						|
          " \"" << "#{base}/#{path}".gsub(/["?<>\*]/, '') << "\""
 | 
						|
        end
 | 
						|
            
 | 
						|
        def logger
 | 
						|
          RAILS_DEFAULT_LOGGER
 | 
						|
        end
 | 
						|
      
 | 
						|
        def shellout(cmd, &block)
 | 
						|
          logger.debug "Shelling out: #{cmd}" if logger && logger.debug?
 | 
						|
          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
 | 
						|
            logger.error("SCM command failed: #{cmd}\n  with: #{e.message}")
 | 
						|
            raise CommandFailed
 | 
						|
          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|
 | 
						|
                if line =~ /^(---|\+\+\+) (.*)$/
 | 
						|
                    self << diff_table if diff_table.length > 1
 | 
						|
                    diff_table = DiffTable.new type
 | 
						|
                end
 | 
						|
                a = diff_table.add_line line
 | 
						|
            end
 | 
						|
            self << diff_table unless diff_table.empty?
 | 
						|
            self
 | 
						|
        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)
 | 
						|
        def initialize(type="inline")
 | 
						|
          @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
 | 
						|
            if line =~ /^(---|\+\+\+) (.*)$/
 | 
						|
              @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)
 | 
						|
            CGI.escapeHTML(line)
 | 
						|
        end
 | 
						|
    
 | 
						|
        def parse_line(line, type="inline")
 | 
						|
          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
 | 
						|
    
 | 
						|
      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
 | 
						|
    end
 | 
						|
  end
 | 
						|
end
 |