comparison lib/redmine/scm/adapters/.svn/text-base/abstract_adapter.rb.svn-base @ 0:513646585e45

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