##// END OF EJS Templates
Fixed that changesets parents associations are not deleted when deleting a repository....
Jean-Philippe Lang -
r8727:8f921216067c
parent child
Show More
@@ -1,412 +1,418
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 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 class ScmFetchError < Exception; end
18 class ScmFetchError < Exception; end
19
19
20 class Repository < ActiveRecord::Base
20 class Repository < ActiveRecord::Base
21 include Redmine::Ciphering
21 include Redmine::Ciphering
22
22
23 belongs_to :project
23 belongs_to :project
24 has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC"
24 has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC"
25 has_many :changes, :through => :changesets
25 has_many :changes, :through => :changesets
26
26
27 serialize :extra_info
27 serialize :extra_info
28
28
29 before_save :check_default
29 before_save :check_default
30
30
31 # Raw SQL to delete changesets and changes in the database
31 # Raw SQL to delete changesets and changes in the database
32 # has_many :changesets, :dependent => :destroy is too slow for big repositories
32 # has_many :changesets, :dependent => :destroy is too slow for big repositories
33 before_destroy :clear_changesets
33 before_destroy :clear_changesets
34
34
35 validates_length_of :password, :maximum => 255, :allow_nil => true
35 validates_length_of :password, :maximum => 255, :allow_nil => true
36 validates_length_of :identifier, :maximum => 255, :allow_blank => true
36 validates_length_of :identifier, :maximum => 255, :allow_blank => true
37 validates_presence_of :identifier, :unless => Proc.new { |r| r.is_default? || r.set_as_default? }
37 validates_presence_of :identifier, :unless => Proc.new { |r| r.is_default? || r.set_as_default? }
38 validates_uniqueness_of :identifier, :scope => :project_id, :allow_blank => true
38 validates_uniqueness_of :identifier, :scope => :project_id, :allow_blank => true
39 validates_exclusion_of :identifier, :in => %w(show entry raw changes annotate diff show stats graph)
39 validates_exclusion_of :identifier, :in => %w(show entry raw changes annotate diff show stats graph)
40 # donwcase letters, digits, dashes but not digits only
40 # donwcase letters, digits, dashes but not digits only
41 validates_format_of :identifier, :with => /^(?!\d+$)[a-z0-9\-]*$/, :allow_blank => true
41 validates_format_of :identifier, :with => /^(?!\d+$)[a-z0-9\-]*$/, :allow_blank => true
42 # Checks if the SCM is enabled when creating a repository
42 # Checks if the SCM is enabled when creating a repository
43 validate :repo_create_validation, :on => :create
43 validate :repo_create_validation, :on => :create
44
44
45 def repo_create_validation
45 def repo_create_validation
46 unless Setting.enabled_scm.include?(self.class.name.demodulize)
46 unless Setting.enabled_scm.include?(self.class.name.demodulize)
47 errors.add(:type, :invalid)
47 errors.add(:type, :invalid)
48 end
48 end
49 end
49 end
50
50
51 def self.human_attribute_name(attribute_key_name, *args)
51 def self.human_attribute_name(attribute_key_name, *args)
52 attr_name = attribute_key_name
52 attr_name = attribute_key_name
53 if attr_name == "log_encoding"
53 if attr_name == "log_encoding"
54 attr_name = "commit_logs_encoding"
54 attr_name = "commit_logs_encoding"
55 end
55 end
56 super(attr_name, *args)
56 super(attr_name, *args)
57 end
57 end
58
58
59 alias :attributes_without_extra_info= :attributes=
59 alias :attributes_without_extra_info= :attributes=
60 def attributes=(new_attributes, guard_protected_attributes = true)
60 def attributes=(new_attributes, guard_protected_attributes = true)
61 return if new_attributes.nil?
61 return if new_attributes.nil?
62 attributes = new_attributes.dup
62 attributes = new_attributes.dup
63 attributes.stringify_keys!
63 attributes.stringify_keys!
64
64
65 p = {}
65 p = {}
66 p_extra = {}
66 p_extra = {}
67 attributes.each do |k, v|
67 attributes.each do |k, v|
68 if k =~ /^extra_/
68 if k =~ /^extra_/
69 p_extra[k] = v
69 p_extra[k] = v
70 else
70 else
71 p[k] = v
71 p[k] = v
72 end
72 end
73 end
73 end
74
74
75 send :attributes_without_extra_info=, p, guard_protected_attributes
75 send :attributes_without_extra_info=, p, guard_protected_attributes
76 if p_extra.keys.any?
76 if p_extra.keys.any?
77 merge_extra_info(p_extra)
77 merge_extra_info(p_extra)
78 end
78 end
79 end
79 end
80
80
81 # Removes leading and trailing whitespace
81 # Removes leading and trailing whitespace
82 def url=(arg)
82 def url=(arg)
83 write_attribute(:url, arg ? arg.to_s.strip : nil)
83 write_attribute(:url, arg ? arg.to_s.strip : nil)
84 end
84 end
85
85
86 # Removes leading and trailing whitespace
86 # Removes leading and trailing whitespace
87 def root_url=(arg)
87 def root_url=(arg)
88 write_attribute(:root_url, arg ? arg.to_s.strip : nil)
88 write_attribute(:root_url, arg ? arg.to_s.strip : nil)
89 end
89 end
90
90
91 def password
91 def password
92 read_ciphered_attribute(:password)
92 read_ciphered_attribute(:password)
93 end
93 end
94
94
95 def password=(arg)
95 def password=(arg)
96 write_ciphered_attribute(:password, arg)
96 write_ciphered_attribute(:password, arg)
97 end
97 end
98
98
99 def scm_adapter
99 def scm_adapter
100 self.class.scm_adapter_class
100 self.class.scm_adapter_class
101 end
101 end
102
102
103 def scm
103 def scm
104 unless @scm
104 unless @scm
105 @scm = self.scm_adapter.new(url, root_url,
105 @scm = self.scm_adapter.new(url, root_url,
106 login, password, path_encoding)
106 login, password, path_encoding)
107 if root_url.blank? && @scm.root_url.present?
107 if root_url.blank? && @scm.root_url.present?
108 update_attribute(:root_url, @scm.root_url)
108 update_attribute(:root_url, @scm.root_url)
109 end
109 end
110 end
110 end
111 @scm
111 @scm
112 end
112 end
113
113
114 def scm_name
114 def scm_name
115 self.class.scm_name
115 self.class.scm_name
116 end
116 end
117
117
118 def name
118 def name
119 if identifier.present?
119 if identifier.present?
120 identifier
120 identifier
121 elsif is_default?
121 elsif is_default?
122 l(:field_repository_is_default)
122 l(:field_repository_is_default)
123 else
123 else
124 scm_name
124 scm_name
125 end
125 end
126 end
126 end
127
127
128 def identifier_param
128 def identifier_param
129 if is_default?
129 if is_default?
130 nil
130 nil
131 elsif identifier.present?
131 elsif identifier.present?
132 identifier
132 identifier
133 else
133 else
134 id.to_s
134 id.to_s
135 end
135 end
136 end
136 end
137
137
138 def <=>(repository)
138 def <=>(repository)
139 if is_default?
139 if is_default?
140 -1
140 -1
141 elsif repository.is_default?
141 elsif repository.is_default?
142 1
142 1
143 else
143 else
144 identifier <=> repository.identifier
144 identifier <=> repository.identifier
145 end
145 end
146 end
146 end
147
147
148 def self.find_by_identifier_param(param)
148 def self.find_by_identifier_param(param)
149 if param.to_s =~ /^\d+$/
149 if param.to_s =~ /^\d+$/
150 find_by_id(param)
150 find_by_id(param)
151 else
151 else
152 find_by_identifier(param)
152 find_by_identifier(param)
153 end
153 end
154 end
154 end
155
155
156 def merge_extra_info(arg)
156 def merge_extra_info(arg)
157 h = extra_info || {}
157 h = extra_info || {}
158 return h if arg.nil?
158 return h if arg.nil?
159 h.merge!(arg)
159 h.merge!(arg)
160 write_attribute(:extra_info, h)
160 write_attribute(:extra_info, h)
161 end
161 end
162
162
163 def report_last_commit
163 def report_last_commit
164 true
164 true
165 end
165 end
166
166
167 def supports_cat?
167 def supports_cat?
168 scm.supports_cat?
168 scm.supports_cat?
169 end
169 end
170
170
171 def supports_annotate?
171 def supports_annotate?
172 scm.supports_annotate?
172 scm.supports_annotate?
173 end
173 end
174
174
175 def supports_all_revisions?
175 def supports_all_revisions?
176 true
176 true
177 end
177 end
178
178
179 def supports_directory_revisions?
179 def supports_directory_revisions?
180 false
180 false
181 end
181 end
182
182
183 def supports_revision_graph?
183 def supports_revision_graph?
184 false
184 false
185 end
185 end
186
186
187 def entry(path=nil, identifier=nil)
187 def entry(path=nil, identifier=nil)
188 scm.entry(path, identifier)
188 scm.entry(path, identifier)
189 end
189 end
190
190
191 def entries(path=nil, identifier=nil)
191 def entries(path=nil, identifier=nil)
192 scm.entries(path, identifier)
192 scm.entries(path, identifier)
193 end
193 end
194
194
195 def branches
195 def branches
196 scm.branches
196 scm.branches
197 end
197 end
198
198
199 def tags
199 def tags
200 scm.tags
200 scm.tags
201 end
201 end
202
202
203 def default_branch
203 def default_branch
204 nil
204 nil
205 end
205 end
206
206
207 def properties(path, identifier=nil)
207 def properties(path, identifier=nil)
208 scm.properties(path, identifier)
208 scm.properties(path, identifier)
209 end
209 end
210
210
211 def cat(path, identifier=nil)
211 def cat(path, identifier=nil)
212 scm.cat(path, identifier)
212 scm.cat(path, identifier)
213 end
213 end
214
214
215 def diff(path, rev, rev_to)
215 def diff(path, rev, rev_to)
216 scm.diff(path, rev, rev_to)
216 scm.diff(path, rev, rev_to)
217 end
217 end
218
218
219 def diff_format_revisions(cs, cs_to, sep=':')
219 def diff_format_revisions(cs, cs_to, sep=':')
220 text = ""
220 text = ""
221 text << cs_to.format_identifier + sep if cs_to
221 text << cs_to.format_identifier + sep if cs_to
222 text << cs.format_identifier if cs
222 text << cs.format_identifier if cs
223 text
223 text
224 end
224 end
225
225
226 # Returns a path relative to the url of the repository
226 # Returns a path relative to the url of the repository
227 def relative_path(path)
227 def relative_path(path)
228 path
228 path
229 end
229 end
230
230
231 # Finds and returns a revision with a number or the beginning of a hash
231 # Finds and returns a revision with a number or the beginning of a hash
232 def find_changeset_by_name(name)
232 def find_changeset_by_name(name)
233 return nil if name.blank?
233 return nil if name.blank?
234 changesets.find(:first, :conditions => (name.match(/^\d*$/) ?
234 changesets.find(:first, :conditions => (name.match(/^\d*$/) ?
235 ["revision = ?", name.to_s] : ["revision LIKE ?", name + '%']))
235 ["revision = ?", name.to_s] : ["revision LIKE ?", name + '%']))
236 end
236 end
237
237
238 def latest_changeset
238 def latest_changeset
239 @latest_changeset ||= changesets.find(:first)
239 @latest_changeset ||= changesets.find(:first)
240 end
240 end
241
241
242 # Returns the latest changesets for +path+
242 # Returns the latest changesets for +path+
243 # Default behaviour is to search in cached changesets
243 # Default behaviour is to search in cached changesets
244 def latest_changesets(path, rev, limit=10)
244 def latest_changesets(path, rev, limit=10)
245 if path.blank?
245 if path.blank?
246 changesets.find(
246 changesets.find(
247 :all,
247 :all,
248 :include => :user,
248 :include => :user,
249 :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
249 :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
250 :limit => limit)
250 :limit => limit)
251 else
251 else
252 changes.find(
252 changes.find(
253 :all,
253 :all,
254 :include => {:changeset => :user},
254 :include => {:changeset => :user},
255 :conditions => ["path = ?", path.with_leading_slash],
255 :conditions => ["path = ?", path.with_leading_slash],
256 :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
256 :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
257 :limit => limit
257 :limit => limit
258 ).collect(&:changeset)
258 ).collect(&:changeset)
259 end
259 end
260 end
260 end
261
261
262 def scan_changesets_for_issue_ids
262 def scan_changesets_for_issue_ids
263 self.changesets.each(&:scan_comment_for_issue_ids)
263 self.changesets.each(&:scan_comment_for_issue_ids)
264 end
264 end
265
265
266 # Returns an array of committers usernames and associated user_id
266 # Returns an array of committers usernames and associated user_id
267 def committers
267 def committers
268 @committers ||= Changeset.connection.select_rows(
268 @committers ||= Changeset.connection.select_rows(
269 "SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}")
269 "SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}")
270 end
270 end
271
271
272 # Maps committers username to a user ids
272 # Maps committers username to a user ids
273 def committer_ids=(h)
273 def committer_ids=(h)
274 if h.is_a?(Hash)
274 if h.is_a?(Hash)
275 committers.each do |committer, user_id|
275 committers.each do |committer, user_id|
276 new_user_id = h[committer]
276 new_user_id = h[committer]
277 if new_user_id && (new_user_id.to_i != user_id.to_i)
277 if new_user_id && (new_user_id.to_i != user_id.to_i)
278 new_user_id = (new_user_id.to_i > 0 ? new_user_id.to_i : nil)
278 new_user_id = (new_user_id.to_i > 0 ? new_user_id.to_i : nil)
279 Changeset.update_all(
279 Changeset.update_all(
280 "user_id = #{ new_user_id.nil? ? 'NULL' : new_user_id }",
280 "user_id = #{ new_user_id.nil? ? 'NULL' : new_user_id }",
281 ["repository_id = ? AND committer = ?", id, committer])
281 ["repository_id = ? AND committer = ?", id, committer])
282 end
282 end
283 end
283 end
284 @committers = nil
284 @committers = nil
285 @found_committer_users = nil
285 @found_committer_users = nil
286 true
286 true
287 else
287 else
288 false
288 false
289 end
289 end
290 end
290 end
291
291
292 # Returns the Redmine User corresponding to the given +committer+
292 # Returns the Redmine User corresponding to the given +committer+
293 # It will return nil if the committer is not yet mapped and if no User
293 # It will return nil if the committer is not yet mapped and if no User
294 # with the same username or email was found
294 # with the same username or email was found
295 def find_committer_user(committer)
295 def find_committer_user(committer)
296 unless committer.blank?
296 unless committer.blank?
297 @found_committer_users ||= {}
297 @found_committer_users ||= {}
298 return @found_committer_users[committer] if @found_committer_users.has_key?(committer)
298 return @found_committer_users[committer] if @found_committer_users.has_key?(committer)
299
299
300 user = nil
300 user = nil
301 c = changesets.find(:first, :conditions => {:committer => committer}, :include => :user)
301 c = changesets.find(:first, :conditions => {:committer => committer}, :include => :user)
302 if c && c.user
302 if c && c.user
303 user = c.user
303 user = c.user
304 elsif committer.strip =~ /^([^<]+)(<(.*)>)?$/
304 elsif committer.strip =~ /^([^<]+)(<(.*)>)?$/
305 username, email = $1.strip, $3
305 username, email = $1.strip, $3
306 u = User.find_by_login(username)
306 u = User.find_by_login(username)
307 u ||= User.find_by_mail(email) unless email.blank?
307 u ||= User.find_by_mail(email) unless email.blank?
308 user = u
308 user = u
309 end
309 end
310 @found_committer_users[committer] = user
310 @found_committer_users[committer] = user
311 user
311 user
312 end
312 end
313 end
313 end
314
314
315 def repo_log_encoding
315 def repo_log_encoding
316 encoding = log_encoding.to_s.strip
316 encoding = log_encoding.to_s.strip
317 encoding.blank? ? 'UTF-8' : encoding
317 encoding.blank? ? 'UTF-8' : encoding
318 end
318 end
319
319
320 # Fetches new changesets for all repositories of active projects
320 # Fetches new changesets for all repositories of active projects
321 # Can be called periodically by an external script
321 # Can be called periodically by an external script
322 # eg. ruby script/runner "Repository.fetch_changesets"
322 # eg. ruby script/runner "Repository.fetch_changesets"
323 def self.fetch_changesets
323 def self.fetch_changesets
324 Project.active.has_module(:repository).all.each do |project|
324 Project.active.has_module(:repository).all.each do |project|
325 project.repositories.each do |repository|
325 project.repositories.each do |repository|
326 begin
326 begin
327 repository.fetch_changesets
327 repository.fetch_changesets
328 rescue Redmine::Scm::Adapters::CommandFailed => e
328 rescue Redmine::Scm::Adapters::CommandFailed => e
329 logger.error "scm: error during fetching changesets: #{e.message}"
329 logger.error "scm: error during fetching changesets: #{e.message}"
330 end
330 end
331 end
331 end
332 end
332 end
333 end
333 end
334
334
335 # scan changeset comments to find related and fixed issues for all repositories
335 # scan changeset comments to find related and fixed issues for all repositories
336 def self.scan_changesets_for_issue_ids
336 def self.scan_changesets_for_issue_ids
337 find(:all).each(&:scan_changesets_for_issue_ids)
337 find(:all).each(&:scan_changesets_for_issue_ids)
338 end
338 end
339
339
340 def self.scm_name
340 def self.scm_name
341 'Abstract'
341 'Abstract'
342 end
342 end
343
343
344 def self.available_scm
344 def self.available_scm
345 subclasses.collect {|klass| [klass.scm_name, klass.name]}
345 subclasses.collect {|klass| [klass.scm_name, klass.name]}
346 end
346 end
347
347
348 def self.factory(klass_name, *args)
348 def self.factory(klass_name, *args)
349 klass = "Repository::#{klass_name}".constantize
349 klass = "Repository::#{klass_name}".constantize
350 klass.new(*args)
350 klass.new(*args)
351 rescue
351 rescue
352 nil
352 nil
353 end
353 end
354
354
355 def self.scm_adapter_class
355 def self.scm_adapter_class
356 nil
356 nil
357 end
357 end
358
358
359 def self.scm_command
359 def self.scm_command
360 ret = ""
360 ret = ""
361 begin
361 begin
362 ret = self.scm_adapter_class.client_command if self.scm_adapter_class
362 ret = self.scm_adapter_class.client_command if self.scm_adapter_class
363 rescue Exception => e
363 rescue Exception => e
364 logger.error "scm: error during get command: #{e.message}"
364 logger.error "scm: error during get command: #{e.message}"
365 end
365 end
366 ret
366 ret
367 end
367 end
368
368
369 def self.scm_version_string
369 def self.scm_version_string
370 ret = ""
370 ret = ""
371 begin
371 begin
372 ret = self.scm_adapter_class.client_version_string if self.scm_adapter_class
372 ret = self.scm_adapter_class.client_version_string if self.scm_adapter_class
373 rescue Exception => e
373 rescue Exception => e
374 logger.error "scm: error during get version string: #{e.message}"
374 logger.error "scm: error during get version string: #{e.message}"
375 end
375 end
376 ret
376 ret
377 end
377 end
378
378
379 def self.scm_available
379 def self.scm_available
380 ret = false
380 ret = false
381 begin
381 begin
382 ret = self.scm_adapter_class.client_available if self.scm_adapter_class
382 ret = self.scm_adapter_class.client_available if self.scm_adapter_class
383 rescue Exception => e
383 rescue Exception => e
384 logger.error "scm: error during get scm available: #{e.message}"
384 logger.error "scm: error during get scm available: #{e.message}"
385 end
385 end
386 ret
386 ret
387 end
387 end
388
388
389 def set_as_default?
389 def set_as_default?
390 new_record? && project && !Repository.first(:conditions => {:project_id => project.id})
390 new_record? && project && !Repository.first(:conditions => {:project_id => project.id})
391 end
391 end
392
392
393 protected
393 protected
394
394
395 def check_default
395 def check_default
396 if !is_default? && set_as_default?
396 if !is_default? && set_as_default?
397 self.is_default = true
397 self.is_default = true
398 end
398 end
399 if is_default? && is_default_changed?
399 if is_default? && is_default_changed?
400 Repository.update_all(["is_default = ?", false], ["project_id = ?", project_id])
400 Repository.update_all(["is_default = ?", false], ["project_id = ?", project_id])
401 end
401 end
402 end
402 end
403
403
404 private
404 private
405
405
406 # Deletes repository data
406 def clear_changesets
407 def clear_changesets
407 cs, ch, ci = Changeset.table_name, Change.table_name, "#{table_name_prefix}changesets_issues#{table_name_suffix}"
408 cs = Changeset.table_name
409 ch = Change.table_name
410 ci = "#{table_name_prefix}changesets_issues#{table_name_suffix}"
411 cp = "#{table_name_prefix}changeset_parents#{table_name_suffix}"
412
408 connection.delete("DELETE FROM #{ch} WHERE #{ch}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
413 connection.delete("DELETE FROM #{ch} WHERE #{ch}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
409 connection.delete("DELETE FROM #{ci} WHERE #{ci}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
414 connection.delete("DELETE FROM #{ci} WHERE #{ci}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
415 connection.delete("DELETE FROM #{cp} WHERE #{cp}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
410 connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}")
416 connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}")
411 end
417 end
412 end
418 end
@@ -1,269 +1,278
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 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 File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class RepositoryTest < ActiveSupport::TestCase
20 class RepositoryTest < ActiveSupport::TestCase
21 fixtures :projects,
21 fixtures :projects,
22 :trackers,
22 :trackers,
23 :projects_trackers,
23 :projects_trackers,
24 :enabled_modules,
24 :enabled_modules,
25 :repositories,
25 :repositories,
26 :issues,
26 :issues,
27 :issue_statuses,
27 :issue_statuses,
28 :issue_categories,
28 :issue_categories,
29 :changesets,
29 :changesets,
30 :changes,
30 :changes,
31 :users,
31 :users,
32 :members,
32 :members,
33 :member_roles,
33 :member_roles,
34 :roles,
34 :roles,
35 :enumerations
35 :enumerations
36
36
37 def setup
37 def setup
38 @repository = Project.find(1).repository
38 @repository = Project.find(1).repository
39 end
39 end
40
40
41 def test_create
41 def test_create
42 repository = Repository::Subversion.new(:project => Project.find(3))
42 repository = Repository::Subversion.new(:project => Project.find(3))
43 assert !repository.save
43 assert !repository.save
44
44
45 repository.url = "svn://localhost"
45 repository.url = "svn://localhost"
46 assert repository.save
46 assert repository.save
47 repository.reload
47 repository.reload
48
48
49 project = Project.find(3)
49 project = Project.find(3)
50 assert_equal repository, project.repository
50 assert_equal repository, project.repository
51 end
51 end
52
52
53 def test_first_repository_should_be_set_as_default
53 def test_first_repository_should_be_set_as_default
54 repository1 = Repository::Subversion.new(:project => Project.find(3), :identifier => 'svn1', :url => 'file:///svn1')
54 repository1 = Repository::Subversion.new(:project => Project.find(3), :identifier => 'svn1', :url => 'file:///svn1')
55 assert repository1.save
55 assert repository1.save
56 assert repository1.is_default?
56 assert repository1.is_default?
57
57
58 repository2 = Repository::Subversion.new(:project => Project.find(3), :identifier => 'svn2', :url => 'file:///svn2')
58 repository2 = Repository::Subversion.new(:project => Project.find(3), :identifier => 'svn2', :url => 'file:///svn2')
59 assert repository2.save
59 assert repository2.save
60 assert !repository2.is_default?
60 assert !repository2.is_default?
61
61
62 assert_equal repository1, Project.find(3).repository
62 assert_equal repository1, Project.find(3).repository
63 assert_equal [repository1, repository2], Project.find(3).repositories.sort
63 assert_equal [repository1, repository2], Project.find(3).repositories.sort
64 end
64 end
65
65
66 def test_destroy
66 def test_destroy
67 changesets = Changeset.count(:all, :conditions => "repository_id = 10")
67 changesets = Changeset.count(:all, :conditions => "repository_id = 10")
68 changes = Change.count(:all, :conditions => "repository_id = 10",
68 changes = Change.count(:all, :conditions => "repository_id = 10",
69 :include => :changeset)
69 :include => :changeset)
70 assert_difference 'Changeset.count', -changesets do
70 assert_difference 'Changeset.count', -changesets do
71 assert_difference 'Change.count', -changes do
71 assert_difference 'Change.count', -changes do
72 Repository.find(10).destroy
72 Repository.find(10).destroy
73 end
73 end
74 end
74 end
75 end
75 end
76
76
77 def test_destroy_should_delete_parents_associations
78 changeset = Changeset.find(102)
79 changeset.parents = Changeset.find_all_by_id([100, 101])
80
81 assert_difference 'Changeset.connection.select_all("select * from changeset_parents").size', -2 do
82 Repository.find(10).destroy
83 end
84 end
85
77 def test_should_not_create_with_disabled_scm
86 def test_should_not_create_with_disabled_scm
78 # disable Subversion
87 # disable Subversion
79 with_settings :enabled_scm => ['Darcs', 'Git'] do
88 with_settings :enabled_scm => ['Darcs', 'Git'] do
80 repository = Repository::Subversion.new(
89 repository = Repository::Subversion.new(
81 :project => Project.find(3), :url => "svn://localhost")
90 :project => Project.find(3), :url => "svn://localhost")
82 assert !repository.save
91 assert !repository.save
83 assert_equal I18n.translate('activerecord.errors.messages.invalid'),
92 assert_equal I18n.translate('activerecord.errors.messages.invalid'),
84 repository.errors[:type].to_s
93 repository.errors[:type].to_s
85 end
94 end
86 end
95 end
87
96
88 def test_scan_changesets_for_issue_ids
97 def test_scan_changesets_for_issue_ids
89 Setting.default_language = 'en'
98 Setting.default_language = 'en'
90 Setting.notified_events = ['issue_added','issue_updated']
99 Setting.notified_events = ['issue_added','issue_updated']
91
100
92 # choosing a status to apply to fix issues
101 # choosing a status to apply to fix issues
93 Setting.commit_fix_status_id = IssueStatus.find(
102 Setting.commit_fix_status_id = IssueStatus.find(
94 :first,
103 :first,
95 :conditions => ["is_closed = ?", true]).id
104 :conditions => ["is_closed = ?", true]).id
96 Setting.commit_fix_done_ratio = "90"
105 Setting.commit_fix_done_ratio = "90"
97 Setting.commit_ref_keywords = 'refs , references, IssueID'
106 Setting.commit_ref_keywords = 'refs , references, IssueID'
98 Setting.commit_fix_keywords = 'fixes , closes'
107 Setting.commit_fix_keywords = 'fixes , closes'
99 Setting.default_language = 'en'
108 Setting.default_language = 'en'
100 ActionMailer::Base.deliveries.clear
109 ActionMailer::Base.deliveries.clear
101
110
102 # make sure issue 1 is not already closed
111 # make sure issue 1 is not already closed
103 fixed_issue = Issue.find(1)
112 fixed_issue = Issue.find(1)
104 assert !fixed_issue.status.is_closed?
113 assert !fixed_issue.status.is_closed?
105 old_status = fixed_issue.status
114 old_status = fixed_issue.status
106
115
107 Repository.scan_changesets_for_issue_ids
116 Repository.scan_changesets_for_issue_ids
108 assert_equal [101, 102], Issue.find(3).changeset_ids
117 assert_equal [101, 102], Issue.find(3).changeset_ids
109
118
110 # fixed issues
119 # fixed issues
111 fixed_issue.reload
120 fixed_issue.reload
112 assert fixed_issue.status.is_closed?
121 assert fixed_issue.status.is_closed?
113 assert_equal 90, fixed_issue.done_ratio
122 assert_equal 90, fixed_issue.done_ratio
114 assert_equal [101], fixed_issue.changeset_ids
123 assert_equal [101], fixed_issue.changeset_ids
115
124
116 # issue change
125 # issue change
117 journal = fixed_issue.journals.find(:first, :order => 'created_on desc')
126 journal = fixed_issue.journals.find(:first, :order => 'created_on desc')
118 assert_equal User.find_by_login('dlopper'), journal.user
127 assert_equal User.find_by_login('dlopper'), journal.user
119 assert_equal 'Applied in changeset r2.', journal.notes
128 assert_equal 'Applied in changeset r2.', journal.notes
120
129
121 # 2 email notifications
130 # 2 email notifications
122 assert_equal 2, ActionMailer::Base.deliveries.size
131 assert_equal 2, ActionMailer::Base.deliveries.size
123 mail = ActionMailer::Base.deliveries.first
132 mail = ActionMailer::Base.deliveries.first
124 assert_kind_of TMail::Mail, mail
133 assert_kind_of TMail::Mail, mail
125 assert mail.subject.starts_with?(
134 assert mail.subject.starts_with?(
126 "[#{fixed_issue.project.name} - #{fixed_issue.tracker.name} ##{fixed_issue.id}]")
135 "[#{fixed_issue.project.name} - #{fixed_issue.tracker.name} ##{fixed_issue.id}]")
127 assert mail.body.include?(
136 assert mail.body.include?(
128 "Status changed from #{old_status} to #{fixed_issue.status}")
137 "Status changed from #{old_status} to #{fixed_issue.status}")
129
138
130 # ignoring commits referencing an issue of another project
139 # ignoring commits referencing an issue of another project
131 assert_equal [], Issue.find(4).changesets
140 assert_equal [], Issue.find(4).changesets
132 end
141 end
133
142
134 def test_for_changeset_comments_strip
143 def test_for_changeset_comments_strip
135 repository = Repository::Mercurial.create(
144 repository = Repository::Mercurial.create(
136 :project => Project.find( 4 ),
145 :project => Project.find( 4 ),
137 :url => '/foo/bar/baz' )
146 :url => '/foo/bar/baz' )
138 comment = <<-COMMENT
147 comment = <<-COMMENT
139 This is a loooooooooooooooooooooooooooong comment
148 This is a loooooooooooooooooooooooooooong comment
140
149
141
150
142 COMMENT
151 COMMENT
143 changeset = Changeset.new(
152 changeset = Changeset.new(
144 :comments => comment, :commit_date => Time.now,
153 :comments => comment, :commit_date => Time.now,
145 :revision => 0, :scmid => 'f39b7922fb3c',
154 :revision => 0, :scmid => 'f39b7922fb3c',
146 :committer => 'foo <foo@example.com>',
155 :committer => 'foo <foo@example.com>',
147 :committed_on => Time.now, :repository => repository )
156 :committed_on => Time.now, :repository => repository )
148 assert( changeset.save )
157 assert( changeset.save )
149 assert_not_equal( comment, changeset.comments )
158 assert_not_equal( comment, changeset.comments )
150 assert_equal( 'This is a loooooooooooooooooooooooooooong comment',
159 assert_equal( 'This is a loooooooooooooooooooooooooooong comment',
151 changeset.comments )
160 changeset.comments )
152 end
161 end
153
162
154 def test_for_urls_strip_cvs
163 def test_for_urls_strip_cvs
155 repository = Repository::Cvs.create(
164 repository = Repository::Cvs.create(
156 :project => Project.find(4),
165 :project => Project.find(4),
157 :url => ' :pserver:login:password@host:/path/to/the/repository',
166 :url => ' :pserver:login:password@host:/path/to/the/repository',
158 :root_url => 'foo ',
167 :root_url => 'foo ',
159 :log_encoding => 'UTF-8')
168 :log_encoding => 'UTF-8')
160 assert repository.save
169 assert repository.save
161 repository.reload
170 repository.reload
162 assert_equal ':pserver:login:password@host:/path/to/the/repository',
171 assert_equal ':pserver:login:password@host:/path/to/the/repository',
163 repository.url
172 repository.url
164 assert_equal 'foo', repository.root_url
173 assert_equal 'foo', repository.root_url
165 end
174 end
166
175
167 def test_for_urls_strip_subversion
176 def test_for_urls_strip_subversion
168 repository = Repository::Subversion.create(
177 repository = Repository::Subversion.create(
169 :project => Project.find(4),
178 :project => Project.find(4),
170 :url => ' file:///dummy ')
179 :url => ' file:///dummy ')
171 assert repository.save
180 assert repository.save
172 repository.reload
181 repository.reload
173 assert_equal 'file:///dummy', repository.url
182 assert_equal 'file:///dummy', repository.url
174 end
183 end
175
184
176 def test_for_urls_strip_git
185 def test_for_urls_strip_git
177 repository = Repository::Git.create(
186 repository = Repository::Git.create(
178 :project => Project.find(4),
187 :project => Project.find(4),
179 :url => ' c:\dummy ')
188 :url => ' c:\dummy ')
180 assert repository.save
189 assert repository.save
181 repository.reload
190 repository.reload
182 assert_equal 'c:\dummy', repository.url
191 assert_equal 'c:\dummy', repository.url
183 end
192 end
184
193
185 def test_manual_user_mapping
194 def test_manual_user_mapping
186 assert_no_difference "Changeset.count(:conditions => 'user_id <> 2')" do
195 assert_no_difference "Changeset.count(:conditions => 'user_id <> 2')" do
187 c = Changeset.create!(
196 c = Changeset.create!(
188 :repository => @repository,
197 :repository => @repository,
189 :committer => 'foo',
198 :committer => 'foo',
190 :committed_on => Time.now,
199 :committed_on => Time.now,
191 :revision => 100,
200 :revision => 100,
192 :comments => 'Committed by foo.'
201 :comments => 'Committed by foo.'
193 )
202 )
194 assert_nil c.user
203 assert_nil c.user
195 @repository.committer_ids = {'foo' => '2'}
204 @repository.committer_ids = {'foo' => '2'}
196 assert_equal User.find(2), c.reload.user
205 assert_equal User.find(2), c.reload.user
197 # committer is now mapped
206 # committer is now mapped
198 c = Changeset.create!(
207 c = Changeset.create!(
199 :repository => @repository,
208 :repository => @repository,
200 :committer => 'foo',
209 :committer => 'foo',
201 :committed_on => Time.now,
210 :committed_on => Time.now,
202 :revision => 101,
211 :revision => 101,
203 :comments => 'Another commit by foo.'
212 :comments => 'Another commit by foo.'
204 )
213 )
205 assert_equal User.find(2), c.user
214 assert_equal User.find(2), c.user
206 end
215 end
207 end
216 end
208
217
209 def test_auto_user_mapping_by_username
218 def test_auto_user_mapping_by_username
210 c = Changeset.create!(
219 c = Changeset.create!(
211 :repository => @repository,
220 :repository => @repository,
212 :committer => 'jsmith',
221 :committer => 'jsmith',
213 :committed_on => Time.now,
222 :committed_on => Time.now,
214 :revision => 100,
223 :revision => 100,
215 :comments => 'Committed by john.'
224 :comments => 'Committed by john.'
216 )
225 )
217 assert_equal User.find(2), c.user
226 assert_equal User.find(2), c.user
218 end
227 end
219
228
220 def test_auto_user_mapping_by_email
229 def test_auto_user_mapping_by_email
221 c = Changeset.create!(
230 c = Changeset.create!(
222 :repository => @repository,
231 :repository => @repository,
223 :committer => 'john <jsmith@somenet.foo>',
232 :committer => 'john <jsmith@somenet.foo>',
224 :committed_on => Time.now,
233 :committed_on => Time.now,
225 :revision => 100,
234 :revision => 100,
226 :comments => 'Committed by john.'
235 :comments => 'Committed by john.'
227 )
236 )
228 assert_equal User.find(2), c.user
237 assert_equal User.find(2), c.user
229 end
238 end
230
239
231 def test_filesystem_avaialbe
240 def test_filesystem_avaialbe
232 klass = Repository::Filesystem
241 klass = Repository::Filesystem
233 assert klass.scm_adapter_class
242 assert klass.scm_adapter_class
234 assert_equal true, klass.scm_available
243 assert_equal true, klass.scm_available
235 end
244 end
236
245
237 def test_merge_extra_info
246 def test_merge_extra_info
238 repo = Repository::Subversion.new(:project => Project.find(3))
247 repo = Repository::Subversion.new(:project => Project.find(3))
239 assert !repo.save
248 assert !repo.save
240 repo.url = "svn://localhost"
249 repo.url = "svn://localhost"
241 assert repo.save
250 assert repo.save
242 repo.reload
251 repo.reload
243 project = Project.find(3)
252 project = Project.find(3)
244 assert_equal repo, project.repository
253 assert_equal repo, project.repository
245 assert_nil repo.extra_info
254 assert_nil repo.extra_info
246 h1 = {"test_1" => {"test_11" => "test_value_11"}}
255 h1 = {"test_1" => {"test_11" => "test_value_11"}}
247 repo.merge_extra_info(h1)
256 repo.merge_extra_info(h1)
248 assert_equal h1, repo.extra_info
257 assert_equal h1, repo.extra_info
249 h2 = {"test_2" => {
258 h2 = {"test_2" => {
250 "test_21" => "test_value_21",
259 "test_21" => "test_value_21",
251 "test_22" => "test_value_22",
260 "test_22" => "test_value_22",
252 }}
261 }}
253 repo.merge_extra_info(h2)
262 repo.merge_extra_info(h2)
254 assert_equal (h = {"test_11" => "test_value_11"}),
263 assert_equal (h = {"test_11" => "test_value_11"}),
255 repo.extra_info["test_1"]
264 repo.extra_info["test_1"]
256 assert_equal "test_value_21",
265 assert_equal "test_value_21",
257 repo.extra_info["test_2"]["test_21"]
266 repo.extra_info["test_2"]["test_21"]
258 h3 = {"test_2" => {
267 h3 = {"test_2" => {
259 "test_23" => "test_value_23",
268 "test_23" => "test_value_23",
260 "test_24" => "test_value_24",
269 "test_24" => "test_value_24",
261 }}
270 }}
262 repo.merge_extra_info(h3)
271 repo.merge_extra_info(h3)
263 assert_equal (h = {"test_11" => "test_value_11"}),
272 assert_equal (h = {"test_11" => "test_value_11"}),
264 repo.extra_info["test_1"]
273 repo.extra_info["test_1"]
265 assert_nil repo.extra_info["test_2"]["test_21"]
274 assert_nil repo.extra_info["test_2"]["test_21"]
266 assert_equal "test_value_23",
275 assert_equal "test_value_23",
267 repo.extra_info["test_2"]["test_23"]
276 repo.extra_info["test_2"]["test_23"]
268 end
277 end
269 end
278 end
General Comments 0
You need to be logged in to leave comments. Login now