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