##// END OF EJS Templates
Rails4: replace deprecated #connection from Repository model...
Toshi MARUYAMA -
r12443:b1d0107ac0fd
parent child
Show More
@@ -1,436 +1,436
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2013 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 class ScmFetchError < Exception; end
19 19
20 20 class Repository < ActiveRecord::Base
21 21 include Redmine::Ciphering
22 22 include Redmine::SafeAttributes
23 23
24 24 # Maximum length for repository identifiers
25 25 IDENTIFIER_MAX_LENGTH = 255
26 26
27 27 belongs_to :project
28 28 has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC"
29 29 has_many :filechanges, :class_name => 'Change', :through => :changesets
30 30
31 31 serialize :extra_info
32 32
33 33 before_save :check_default
34 34
35 35 # Raw SQL to delete changesets and changes in the database
36 36 # has_many :changesets, :dependent => :destroy is too slow for big repositories
37 37 before_destroy :clear_changesets
38 38
39 39 validates_length_of :password, :maximum => 255, :allow_nil => true
40 40 validates_length_of :identifier, :maximum => IDENTIFIER_MAX_LENGTH, :allow_blank => true
41 41 validates_presence_of :identifier, :unless => Proc.new { |r| r.is_default? || r.set_as_default? }
42 42 validates_uniqueness_of :identifier, :scope => :project_id, :allow_blank => true
43 43 validates_exclusion_of :identifier, :in => %w(show entry raw changes annotate diff show stats graph)
44 44 # donwcase letters, digits, dashes, underscores but not digits only
45 45 validates_format_of :identifier, :with => /\A(?!\d+$)[a-z0-9\-_]*\z/, :allow_blank => true
46 46 # Checks if the SCM is enabled when creating a repository
47 47 validate :repo_create_validation, :on => :create
48 48
49 49 safe_attributes 'identifier',
50 50 'login',
51 51 'password',
52 52 'path_encoding',
53 53 'log_encoding',
54 54 'is_default'
55 55
56 56 safe_attributes 'url',
57 57 :if => lambda {|repository, user| repository.new_record?}
58 58
59 59 def repo_create_validation
60 60 unless Setting.enabled_scm.include?(self.class.name.demodulize)
61 61 errors.add(:type, :invalid)
62 62 end
63 63 end
64 64
65 65 def self.human_attribute_name(attribute_key_name, *args)
66 66 attr_name = attribute_key_name.to_s
67 67 if attr_name == "log_encoding"
68 68 attr_name = "commit_logs_encoding"
69 69 end
70 70 super(attr_name, *args)
71 71 end
72 72
73 73 # Removes leading and trailing whitespace
74 74 def url=(arg)
75 75 write_attribute(:url, arg ? arg.to_s.strip : nil)
76 76 end
77 77
78 78 # Removes leading and trailing whitespace
79 79 def root_url=(arg)
80 80 write_attribute(:root_url, arg ? arg.to_s.strip : nil)
81 81 end
82 82
83 83 def password
84 84 read_ciphered_attribute(:password)
85 85 end
86 86
87 87 def password=(arg)
88 88 write_ciphered_attribute(:password, arg)
89 89 end
90 90
91 91 def scm_adapter
92 92 self.class.scm_adapter_class
93 93 end
94 94
95 95 def scm
96 96 unless @scm
97 97 @scm = self.scm_adapter.new(url, root_url,
98 98 login, password, path_encoding)
99 99 if root_url.blank? && @scm.root_url.present?
100 100 update_attribute(:root_url, @scm.root_url)
101 101 end
102 102 end
103 103 @scm
104 104 end
105 105
106 106 def scm_name
107 107 self.class.scm_name
108 108 end
109 109
110 110 def name
111 111 if identifier.present?
112 112 identifier
113 113 elsif is_default?
114 114 l(:field_repository_is_default)
115 115 else
116 116 scm_name
117 117 end
118 118 end
119 119
120 120 def identifier=(identifier)
121 121 super unless identifier_frozen?
122 122 end
123 123
124 124 def identifier_frozen?
125 125 errors[:identifier].blank? && !(new_record? || identifier.blank?)
126 126 end
127 127
128 128 def identifier_param
129 129 if is_default?
130 130 nil
131 131 elsif identifier.present?
132 132 identifier
133 133 else
134 134 id.to_s
135 135 end
136 136 end
137 137
138 138 def <=>(repository)
139 139 if is_default?
140 140 -1
141 141 elsif repository.is_default?
142 142 1
143 143 else
144 144 identifier.to_s <=> repository.identifier.to_s
145 145 end
146 146 end
147 147
148 148 def self.find_by_identifier_param(param)
149 149 if param.to_s =~ /^\d+$/
150 150 find_by_id(param)
151 151 else
152 152 find_by_identifier(param)
153 153 end
154 154 end
155 155
156 156 def merge_extra_info(arg)
157 157 h = extra_info || {}
158 158 return h if arg.nil?
159 159 h.merge!(arg)
160 160 write_attribute(:extra_info, h)
161 161 end
162 162
163 163 def report_last_commit
164 164 true
165 165 end
166 166
167 167 def supports_cat?
168 168 scm.supports_cat?
169 169 end
170 170
171 171 def supports_annotate?
172 172 scm.supports_annotate?
173 173 end
174 174
175 175 def supports_all_revisions?
176 176 true
177 177 end
178 178
179 179 def supports_directory_revisions?
180 180 false
181 181 end
182 182
183 183 def supports_revision_graph?
184 184 false
185 185 end
186 186
187 187 def entry(path=nil, identifier=nil)
188 188 scm.entry(path, identifier)
189 189 end
190 190
191 191 def entries(path=nil, identifier=nil)
192 192 entries = scm.entries(path, identifier)
193 193 load_entries_changesets(entries)
194 194 entries
195 195 end
196 196
197 197 def branches
198 198 scm.branches
199 199 end
200 200
201 201 def tags
202 202 scm.tags
203 203 end
204 204
205 205 def default_branch
206 206 nil
207 207 end
208 208
209 209 def properties(path, identifier=nil)
210 210 scm.properties(path, identifier)
211 211 end
212 212
213 213 def cat(path, identifier=nil)
214 214 scm.cat(path, identifier)
215 215 end
216 216
217 217 def diff(path, rev, rev_to)
218 218 scm.diff(path, rev, rev_to)
219 219 end
220 220
221 221 def diff_format_revisions(cs, cs_to, sep=':')
222 222 text = ""
223 223 text << cs_to.format_identifier + sep if cs_to
224 224 text << cs.format_identifier if cs
225 225 text
226 226 end
227 227
228 228 # Returns a path relative to the url of the repository
229 229 def relative_path(path)
230 230 path
231 231 end
232 232
233 233 # Finds and returns a revision with a number or the beginning of a hash
234 234 def find_changeset_by_name(name)
235 235 return nil if name.blank?
236 236 s = name.to_s
237 237 if s.match(/^\d*$/)
238 238 changesets.where("revision = ?", s).first
239 239 else
240 240 changesets.where("revision LIKE ?", s + '%').first
241 241 end
242 242 end
243 243
244 244 def latest_changeset
245 245 @latest_changeset ||= changesets.first
246 246 end
247 247
248 248 # Returns the latest changesets for +path+
249 249 # Default behaviour is to search in cached changesets
250 250 def latest_changesets(path, rev, limit=10)
251 251 if path.blank?
252 252 changesets.
253 253 reorder("#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC").
254 254 limit(limit).
255 255 preload(:user).
256 256 all
257 257 else
258 258 filechanges.
259 259 where("path = ?", path.with_leading_slash).
260 260 reorder("#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC").
261 261 limit(limit).
262 262 preload(:changeset => :user).
263 263 collect(&:changeset)
264 264 end
265 265 end
266 266
267 267 def scan_changesets_for_issue_ids
268 268 self.changesets.each(&:scan_comment_for_issue_ids)
269 269 end
270 270
271 271 # Returns an array of committers usernames and associated user_id
272 272 def committers
273 273 @committers ||= Changeset.connection.select_rows(
274 274 "SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}")
275 275 end
276 276
277 277 # Maps committers username to a user ids
278 278 def committer_ids=(h)
279 279 if h.is_a?(Hash)
280 280 committers.each do |committer, user_id|
281 281 new_user_id = h[committer]
282 282 if new_user_id && (new_user_id.to_i != user_id.to_i)
283 283 new_user_id = (new_user_id.to_i > 0 ? new_user_id.to_i : nil)
284 284 Changeset.where(["repository_id = ? AND committer = ?", id, committer]).
285 285 update_all("user_id = #{new_user_id.nil? ? 'NULL' : new_user_id}")
286 286 end
287 287 end
288 288 @committers = nil
289 289 @found_committer_users = nil
290 290 true
291 291 else
292 292 false
293 293 end
294 294 end
295 295
296 296 # Returns the Redmine User corresponding to the given +committer+
297 297 # It will return nil if the committer is not yet mapped and if no User
298 298 # with the same username or email was found
299 299 def find_committer_user(committer)
300 300 unless committer.blank?
301 301 @found_committer_users ||= {}
302 302 return @found_committer_users[committer] if @found_committer_users.has_key?(committer)
303 303
304 304 user = nil
305 305 c = changesets.where(:committer => committer).includes(:user).first
306 306 if c && c.user
307 307 user = c.user
308 308 elsif committer.strip =~ /^([^<]+)(<(.*)>)?$/
309 309 username, email = $1.strip, $3
310 310 u = User.find_by_login(username)
311 311 u ||= User.find_by_mail(email) unless email.blank?
312 312 user = u
313 313 end
314 314 @found_committer_users[committer] = user
315 315 user
316 316 end
317 317 end
318 318
319 319 def repo_log_encoding
320 320 encoding = log_encoding.to_s.strip
321 321 encoding.blank? ? 'UTF-8' : encoding
322 322 end
323 323
324 324 # Fetches new changesets for all repositories of active projects
325 325 # Can be called periodically by an external script
326 326 # eg. ruby script/runner "Repository.fetch_changesets"
327 327 def self.fetch_changesets
328 328 Project.active.has_module(:repository).all.each do |project|
329 329 project.repositories.each do |repository|
330 330 begin
331 331 repository.fetch_changesets
332 332 rescue Redmine::Scm::Adapters::CommandFailed => e
333 333 logger.error "scm: error during fetching changesets: #{e.message}"
334 334 end
335 335 end
336 336 end
337 337 end
338 338
339 339 # scan changeset comments to find related and fixed issues for all repositories
340 340 def self.scan_changesets_for_issue_ids
341 341 all.each(&:scan_changesets_for_issue_ids)
342 342 end
343 343
344 344 def self.scm_name
345 345 'Abstract'
346 346 end
347 347
348 348 def self.available_scm
349 349 subclasses.collect {|klass| [klass.scm_name, klass.name]}
350 350 end
351 351
352 352 def self.factory(klass_name, *args)
353 353 klass = "Repository::#{klass_name}".constantize
354 354 klass.new(*args)
355 355 rescue
356 356 nil
357 357 end
358 358
359 359 def self.scm_adapter_class
360 360 nil
361 361 end
362 362
363 363 def self.scm_command
364 364 ret = ""
365 365 begin
366 366 ret = self.scm_adapter_class.client_command if self.scm_adapter_class
367 367 rescue Exception => e
368 368 logger.error "scm: error during get command: #{e.message}"
369 369 end
370 370 ret
371 371 end
372 372
373 373 def self.scm_version_string
374 374 ret = ""
375 375 begin
376 376 ret = self.scm_adapter_class.client_version_string if self.scm_adapter_class
377 377 rescue Exception => e
378 378 logger.error "scm: error during get version string: #{e.message}"
379 379 end
380 380 ret
381 381 end
382 382
383 383 def self.scm_available
384 384 ret = false
385 385 begin
386 386 ret = self.scm_adapter_class.client_available if self.scm_adapter_class
387 387 rescue Exception => e
388 388 logger.error "scm: error during get scm available: #{e.message}"
389 389 end
390 390 ret
391 391 end
392 392
393 393 def set_as_default?
394 394 new_record? && project && Repository.where(:project_id => project.id).empty?
395 395 end
396 396
397 397 protected
398 398
399 399 def check_default
400 400 if !is_default? && set_as_default?
401 401 self.is_default = true
402 402 end
403 403 if is_default? && is_default_changed?
404 404 Repository.where(["project_id = ?", project_id]).update_all(["is_default = ?", false])
405 405 end
406 406 end
407 407
408 408 def load_entries_changesets(entries)
409 409 if entries
410 410 entries.each do |entry|
411 411 if entry.lastrev && entry.lastrev.identifier
412 412 entry.changeset = find_changeset_by_name(entry.lastrev.identifier)
413 413 end
414 414 end
415 415 end
416 416 end
417 417
418 418 private
419 419
420 420 # Deletes repository data
421 421 def clear_changesets
422 422 cs = Changeset.table_name
423 423 ch = Change.table_name
424 424 ci = "#{table_name_prefix}changesets_issues#{table_name_suffix}"
425 425 cp = "#{table_name_prefix}changeset_parents#{table_name_suffix}"
426 426
427 connection.delete("DELETE FROM #{ch} WHERE #{ch}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
428 connection.delete("DELETE FROM #{ci} WHERE #{ci}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
429 connection.delete("DELETE FROM #{cp} WHERE #{cp}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
430 connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}")
427 ActiveRecord::Base.connection.delete("DELETE FROM #{ch} WHERE #{ch}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
428 ActiveRecord::Base.connection.delete("DELETE FROM #{ci} WHERE #{ci}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
429 ActiveRecord::Base.connection.delete("DELETE FROM #{cp} WHERE #{cp}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
430 ActiveRecord::Base.connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}")
431 431 clear_extra_info_of_changesets
432 432 end
433 433
434 434 def clear_extra_info_of_changesets
435 435 end
436 436 end
General Comments 0
You need to be logged in to leave comments. Login now