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