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