annotate lib/redmine/scm/adapters/.svn/text-base/abstract_adapter.rb.svn-base @ 861:b8105f717bf7 bug_182

Close obsolete branch bug_182
author Chris Cannam
date Fri, 10 Jun 2011 16:49:58 +0100
parents 513646585e45
children af80e5618e9b
rev   line source
Chris@0 1 # redMine - project management software
Chris@0 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
Chris@0 3 #
Chris@0 4 # This program is free software; you can redistribute it and/or
Chris@0 5 # modify it under the terms of the GNU General Public License
Chris@0 6 # as published by the Free Software Foundation; either version 2
Chris@0 7 # of the License, or (at your option) any later version.
Chris@0 8 #
Chris@0 9 # This program is distributed in the hope that it will be useful,
Chris@0 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
Chris@0 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Chris@0 12 # GNU General Public License for more details.
Chris@0 13 #
Chris@0 14 # You should have received a copy of the GNU General Public License
Chris@0 15 # along with this program; if not, write to the Free Software
Chris@0 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Chris@0 17
Chris@0 18 require 'cgi'
Chris@0 19
Chris@0 20 module Redmine
Chris@0 21 module Scm
Chris@0 22 module Adapters
Chris@0 23 class CommandFailed < StandardError #:nodoc:
Chris@0 24 end
Chris@0 25
Chris@0 26 class AbstractAdapter #:nodoc:
Chris@0 27 class << self
Chris@0 28 # Returns the version of the scm client
Chris@0 29 # Eg: [1, 5, 0] or [] if unknown
Chris@0 30 def client_version
Chris@0 31 []
Chris@0 32 end
Chris@0 33
Chris@0 34 # Returns the version string of the scm client
Chris@0 35 # Eg: '1.5.0' or 'Unknown version' if unknown
Chris@0 36 def client_version_string
Chris@0 37 v = client_version || 'Unknown version'
Chris@0 38 v.is_a?(Array) ? v.join('.') : v.to_s
Chris@0 39 end
Chris@0 40
Chris@0 41 # Returns true if the current client version is above
Chris@0 42 # or equals the given one
Chris@0 43 # If option is :unknown is set to true, it will return
Chris@0 44 # true if the client version is unknown
Chris@0 45 def client_version_above?(v, options={})
Chris@0 46 ((client_version <=> v) >= 0) || (client_version.empty? && options[:unknown])
Chris@0 47 end
Chris@0 48 end
Chris@0 49
Chris@0 50 def initialize(url, root_url=nil, login=nil, password=nil)
Chris@0 51 @url = url
Chris@0 52 @login = login if login && !login.empty?
Chris@0 53 @password = (password || "") if @login
Chris@0 54 @root_url = root_url.blank? ? retrieve_root_url : root_url
Chris@0 55 end
Chris@0 56
Chris@0 57 def adapter_name
Chris@0 58 'Abstract'
Chris@0 59 end
Chris@0 60
Chris@0 61 def supports_cat?
Chris@0 62 true
Chris@0 63 end
Chris@0 64
Chris@0 65 def supports_annotate?
Chris@0 66 respond_to?('annotate')
Chris@0 67 end
Chris@0 68
Chris@0 69 def root_url
Chris@0 70 @root_url
Chris@0 71 end
Chris@0 72
Chris@0 73 def url
Chris@0 74 @url
Chris@0 75 end
Chris@0 76
Chris@0 77 # get info about the svn repository
Chris@0 78 def info
Chris@0 79 return nil
Chris@0 80 end
Chris@0 81
Chris@0 82 # Returns the entry identified by path and revision identifier
Chris@0 83 # or nil if entry doesn't exist in the repository
Chris@0 84 def entry(path=nil, identifier=nil)
Chris@0 85 parts = path.to_s.split(%r{[\/\\]}).select {|n| !n.blank?}
Chris@0 86 search_path = parts[0..-2].join('/')
Chris@0 87 search_name = parts[-1]
Chris@0 88 if search_path.blank? && search_name.blank?
Chris@0 89 # Root entry
Chris@0 90 Entry.new(:path => '', :kind => 'dir')
Chris@0 91 else
Chris@0 92 # Search for the entry in the parent directory
Chris@0 93 es = entries(search_path, identifier)
Chris@0 94 es ? es.detect {|e| e.name == search_name} : nil
Chris@0 95 end
Chris@0 96 end
Chris@0 97
Chris@0 98 # Returns an Entries collection
Chris@0 99 # or nil if the given path doesn't exist in the repository
Chris@0 100 def entries(path=nil, identifier=nil)
Chris@0 101 return nil
Chris@0 102 end
Chris@0 103
Chris@0 104 def branches
Chris@0 105 return nil
Chris@0 106 end
Chris@0 107
Chris@0 108 def tags
Chris@0 109 return nil
Chris@0 110 end
Chris@0 111
Chris@0 112 def default_branch
Chris@0 113 return nil
Chris@0 114 end
Chris@0 115
Chris@0 116 def properties(path, identifier=nil)
Chris@0 117 return nil
Chris@0 118 end
Chris@0 119
Chris@0 120 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
Chris@0 121 return nil
Chris@0 122 end
Chris@0 123
Chris@0 124 def diff(path, identifier_from, identifier_to=nil)
Chris@0 125 return nil
Chris@0 126 end
Chris@0 127
Chris@0 128 def cat(path, identifier=nil)
Chris@0 129 return nil
Chris@0 130 end
Chris@0 131
Chris@0 132 def with_leading_slash(path)
Chris@0 133 path ||= ''
Chris@0 134 (path[0,1]!="/") ? "/#{path}" : path
Chris@0 135 end
Chris@0 136
Chris@0 137 def with_trailling_slash(path)
Chris@0 138 path ||= ''
Chris@0 139 (path[-1,1] == "/") ? path : "#{path}/"
Chris@0 140 end
Chris@0 141
Chris@0 142 def without_leading_slash(path)
Chris@0 143 path ||= ''
Chris@0 144 path.gsub(%r{^/+}, '')
Chris@0 145 end
Chris@0 146
Chris@0 147 def without_trailling_slash(path)
Chris@0 148 path ||= ''
Chris@0 149 (path[-1,1] == "/") ? path[0..-2] : path
Chris@0 150 end
Chris@0 151
Chris@0 152 def shell_quote(str)
Chris@0 153 if Redmine::Platform.mswin?
Chris@0 154 '"' + str.gsub(/"/, '\\"') + '"'
Chris@0 155 else
Chris@0 156 "'" + str.gsub(/'/, "'\"'\"'") + "'"
Chris@0 157 end
Chris@0 158 end
Chris@0 159
Chris@0 160 private
Chris@0 161 def retrieve_root_url
Chris@0 162 info = self.info
Chris@0 163 info ? info.root_url : nil
Chris@0 164 end
Chris@0 165
Chris@0 166 def target(path)
Chris@0 167 path ||= ''
Chris@0 168 base = path.match(/^\//) ? root_url : url
Chris@0 169 shell_quote("#{base}/#{path}".gsub(/[?<>\*]/, ''))
Chris@0 170 end
Chris@0 171
Chris@0 172 def logger
Chris@0 173 self.class.logger
Chris@0 174 end
Chris@0 175
Chris@0 176 def shellout(cmd, &block)
Chris@0 177 self.class.shellout(cmd, &block)
Chris@0 178 end
Chris@0 179
Chris@0 180 def self.logger
Chris@0 181 RAILS_DEFAULT_LOGGER
Chris@0 182 end
Chris@0 183
Chris@0 184 def self.shellout(cmd, &block)
Chris@0 185 logger.debug "Shelling out: #{strip_credential(cmd)}" if logger && logger.debug?
Chris@0 186 if Rails.env == 'development'
Chris@0 187 # Capture stderr when running in dev environment
Chris@0 188 cmd = "#{cmd} 2>>#{RAILS_ROOT}/log/scm.stderr.log"
Chris@0 189 end
Chris@0 190 begin
Chris@0 191 IO.popen(cmd, "r+") do |io|
Chris@0 192 io.close_write
Chris@0 193 block.call(io) if block_given?
Chris@0 194 end
Chris@0 195 rescue Errno::ENOENT => e
Chris@0 196 msg = strip_credential(e.message)
Chris@0 197 # The command failed, log it and re-raise
Chris@0 198 logger.error("SCM command failed, make sure that your SCM binary (eg. svn) is in PATH (#{ENV['PATH']}): #{strip_credential(cmd)}\n with: #{msg}")
Chris@0 199 raise CommandFailed.new(msg)
Chris@0 200 end
Chris@0 201 end
Chris@0 202
Chris@0 203 # Hides username/password in a given command
Chris@0 204 def self.strip_credential(cmd)
Chris@0 205 q = (Redmine::Platform.mswin? ? '"' : "'")
Chris@0 206 cmd.to_s.gsub(/(\-\-(password|username))\s+(#{q}[^#{q}]+#{q}|[^#{q}]\S+)/, '\\1 xxxx')
Chris@0 207 end
Chris@0 208
Chris@0 209 def strip_credential(cmd)
Chris@0 210 self.class.strip_credential(cmd)
Chris@0 211 end
Chris@0 212 end
Chris@0 213
Chris@0 214 class Entries < Array
Chris@0 215 def sort_by_name
Chris@0 216 sort {|x,y|
Chris@0 217 if x.kind == y.kind
Chris@0 218 x.name.to_s <=> y.name.to_s
Chris@0 219 else
Chris@0 220 x.kind <=> y.kind
Chris@0 221 end
Chris@0 222 }
Chris@0 223 end
Chris@0 224
Chris@0 225 def revisions
Chris@0 226 revisions ||= Revisions.new(collect{|entry| entry.lastrev}.compact)
Chris@0 227 end
Chris@0 228 end
Chris@0 229
Chris@0 230 class Info
Chris@0 231 attr_accessor :root_url, :lastrev
Chris@0 232 def initialize(attributes={})
Chris@0 233 self.root_url = attributes[:root_url] if attributes[:root_url]
Chris@0 234 self.lastrev = attributes[:lastrev]
Chris@0 235 end
Chris@0 236 end
Chris@0 237
Chris@0 238 class Entry
Chris@0 239 attr_accessor :name, :path, :kind, :size, :lastrev
Chris@0 240 def initialize(attributes={})
Chris@0 241 self.name = attributes[:name] if attributes[:name]
Chris@0 242 self.path = attributes[:path] if attributes[:path]
Chris@0 243 self.kind = attributes[:kind] if attributes[:kind]
Chris@0 244 self.size = attributes[:size].to_i if attributes[:size]
Chris@0 245 self.lastrev = attributes[:lastrev]
Chris@0 246 end
Chris@0 247
Chris@0 248 def is_file?
Chris@0 249 'file' == self.kind
Chris@0 250 end
Chris@0 251
Chris@0 252 def is_dir?
Chris@0 253 'dir' == self.kind
Chris@0 254 end
Chris@0 255
Chris@0 256 def is_text?
Chris@0 257 Redmine::MimeType.is_type?('text', name)
Chris@0 258 end
Chris@0 259 end
Chris@0 260
Chris@0 261 class Revisions < Array
Chris@0 262 def latest
Chris@0 263 sort {|x,y|
Chris@0 264 unless x.time.nil? or y.time.nil?
Chris@0 265 x.time <=> y.time
Chris@0 266 else
Chris@0 267 0
Chris@0 268 end
Chris@0 269 }.last
Chris@0 270 end
Chris@0 271 end
Chris@0 272
Chris@0 273 class Revision
Chris@0 274 attr_accessor :identifier, :scmid, :name, :author, :time, :message, :paths, :revision, :branch
Chris@0 275
Chris@0 276 def initialize(attributes={})
Chris@0 277 self.identifier = attributes[:identifier]
Chris@0 278 self.scmid = attributes[:scmid]
Chris@0 279 self.name = attributes[:name] || self.identifier
Chris@0 280 self.author = attributes[:author]
Chris@0 281 self.time = attributes[:time]
Chris@0 282 self.message = attributes[:message] || ""
Chris@0 283 self.paths = attributes[:paths]
Chris@0 284 self.revision = attributes[:revision]
Chris@0 285 self.branch = attributes[:branch]
Chris@0 286 end
Chris@0 287
Chris@0 288 def save(repo)
Chris@0 289 Changeset.transaction do
Chris@0 290 changeset = Changeset.new(
Chris@0 291 :repository => repo,
Chris@0 292 :revision => identifier,
Chris@0 293 :scmid => scmid,
Chris@0 294 :committer => author,
Chris@0 295 :committed_on => time,
Chris@0 296 :comments => message)
Chris@0 297
Chris@0 298 if changeset.save
Chris@0 299 paths.each do |file|
Chris@0 300 Change.create(
Chris@0 301 :changeset => changeset,
Chris@0 302 :action => file[:action],
Chris@0 303 :path => file[:path])
Chris@0 304 end
Chris@0 305 end
Chris@0 306 end
Chris@0 307 end
Chris@0 308 end
Chris@0 309
Chris@0 310 class Annotate
Chris@0 311 attr_reader :lines, :revisions
Chris@0 312
Chris@0 313 def initialize
Chris@0 314 @lines = []
Chris@0 315 @revisions = []
Chris@0 316 end
Chris@0 317
Chris@0 318 def add_line(line, revision)
Chris@0 319 @lines << line
Chris@0 320 @revisions << revision
Chris@0 321 end
Chris@0 322
Chris@0 323 def content
Chris@0 324 content = lines.join("\n")
Chris@0 325 end
Chris@0 326
Chris@0 327 def empty?
Chris@0 328 lines.empty?
Chris@0 329 end
Chris@0 330 end
Chris@0 331 end
Chris@0 332 end
Chris@0 333 end