##// END OF EJS Templates
scm: code clean up repository model....
Toshi MARUYAMA -
r5527:86d9ea32dbd2
parent child
Show More
@@ -1,298 +1,302
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class Repository < ActiveRecord::Base
19 19 include Redmine::Ciphering
20 20
21 21 belongs_to :project
22 22 has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC"
23 23 has_many :changes, :through => :changesets
24 24
25 25 # Raw SQL to delete changesets and changes in the database
26 26 # has_many :changesets, :dependent => :destroy is too slow for big repositories
27 27 before_destroy :clear_changesets
28 28
29 29 validates_length_of :password, :maximum => 255, :allow_nil => true
30 30 # Checks if the SCM is enabled when creating a repository
31 31 validate_on_create { |r| r.errors.add(:type, :invalid) unless Setting.enabled_scm.include?(r.class.name.demodulize) }
32 32
33 33 def self.human_attribute_name(attribute_key_name)
34 34 attr_name = attribute_key_name
35 35 if attr_name == "log_encoding"
36 36 attr_name = "commit_logs_encoding"
37 37 end
38 38 super(attr_name)
39 39 end
40 40
41 41 # Removes leading and trailing whitespace
42 42 def url=(arg)
43 43 write_attribute(:url, arg ? arg.to_s.strip : nil)
44 44 end
45 45
46 46 # Removes leading and trailing whitespace
47 47 def root_url=(arg)
48 48 write_attribute(:root_url, arg ? arg.to_s.strip : nil)
49 49 end
50 50
51 51 def password
52 52 read_ciphered_attribute(:password)
53 53 end
54 54
55 55 def password=(arg)
56 56 write_ciphered_attribute(:password, arg)
57 57 end
58 58
59 59 def scm_adapter
60 60 self.class.scm_adapter_class
61 61 end
62 62
63 63 def scm
64 64 @scm ||= self.scm_adapter.new(url, root_url,
65 65 login, password, path_encoding)
66 66 update_attribute(:root_url, @scm.root_url) if root_url.blank?
67 67 @scm
68 68 end
69 69
70 70 def scm_name
71 71 self.class.scm_name
72 72 end
73 73
74 74 def supports_cat?
75 75 scm.supports_cat?
76 76 end
77 77
78 78 def supports_annotate?
79 79 scm.supports_annotate?
80 80 end
81 81
82 82 def supports_all_revisions?
83 83 true
84 84 end
85 85
86 86 def supports_directory_revisions?
87 87 false
88 88 end
89 89
90 90 def entry(path=nil, identifier=nil)
91 91 scm.entry(path, identifier)
92 92 end
93 93
94 94 def entries(path=nil, identifier=nil)
95 95 scm.entries(path, identifier)
96 96 end
97 97
98 98 def branches
99 99 scm.branches
100 100 end
101 101
102 102 def tags
103 103 scm.tags
104 104 end
105 105
106 106 def default_branch
107 107 scm.default_branch
108 108 end
109 109
110 110 def properties(path, identifier=nil)
111 111 scm.properties(path, identifier)
112 112 end
113 113
114 114 def cat(path, identifier=nil)
115 115 scm.cat(path, identifier)
116 116 end
117 117
118 118 def diff(path, rev, rev_to)
119 119 scm.diff(path, rev, rev_to)
120 120 end
121 121
122 122 def diff_format_revisions(cs, cs_to, sep=':')
123 123 text = ""
124 124 text << cs_to.format_identifier + sep if cs_to
125 125 text << cs.format_identifier if cs
126 126 text
127 127 end
128 128
129 129 # Returns a path relative to the url of the repository
130 130 def relative_path(path)
131 131 path
132 132 end
133 133
134 134 # Finds and returns a revision with a number or the beginning of a hash
135 135 def find_changeset_by_name(name)
136 136 return nil if name.blank?
137 changesets.find(:first, :conditions => (name.match(/^\d*$/) ? ["revision = ?", name.to_s] : ["revision LIKE ?", name + '%']))
137 changesets.find(:first, :conditions => (name.match(/^\d*$/) ?
138 ["revision = ?", name.to_s] : ["revision LIKE ?", name + '%']))
138 139 end
139 140
140 141 def latest_changeset
141 142 @latest_changeset ||= changesets.find(:first)
142 143 end
143 144
144 145 # Returns the latest changesets for +path+
145 146 # Default behaviour is to search in cached changesets
146 147 def latest_changesets(path, rev, limit=10)
147 148 if path.blank?
148 149 changesets.find(:all, :include => :user,
149 150 :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
150 151 :limit => limit)
151 152 else
152 153 changes.find(:all, :include => {:changeset => :user},
153 154 :conditions => ["path = ?", path.with_leading_slash],
154 155 :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
155 156 :limit => limit).collect(&:changeset)
156 157 end
157 158 end
158 159
159 160 def scan_changesets_for_issue_ids
160 161 self.changesets.each(&:scan_comment_for_issue_ids)
161 162 end
162 163
163 164 # Returns an array of committers usernames and associated user_id
164 165 def committers
165 @committers ||= Changeset.connection.select_rows("SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}")
166 @committers ||= Changeset.connection.select_rows(
167 "SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}")
166 168 end
167 169
168 170 # Maps committers username to a user ids
169 171 def committer_ids=(h)
170 172 if h.is_a?(Hash)
171 173 committers.each do |committer, user_id|
172 174 new_user_id = h[committer]
173 175 if new_user_id && (new_user_id.to_i != user_id.to_i)
174 176 new_user_id = (new_user_id.to_i > 0 ? new_user_id.to_i : nil)
175 Changeset.update_all("user_id = #{ new_user_id.nil? ? 'NULL' : new_user_id }", ["repository_id = ? AND committer = ?", id, committer])
177 Changeset.update_all(
178 "user_id = #{ new_user_id.nil? ? 'NULL' : new_user_id }",
179 ["repository_id = ? AND committer = ?", id, committer])
176 180 end
177 181 end
178 182 @committers = nil
179 183 @found_committer_users = nil
180 184 true
181 185 else
182 186 false
183 187 end
184 188 end
185 189
186 190 # Returns the Redmine User corresponding to the given +committer+
187 191 # It will return nil if the committer is not yet mapped and if no User
188 192 # with the same username or email was found
189 193 def find_committer_user(committer)
190 194 unless committer.blank?
191 195 @found_committer_users ||= {}
192 196 return @found_committer_users[committer] if @found_committer_users.has_key?(committer)
193 197
194 198 user = nil
195 199 c = changesets.find(:first, :conditions => {:committer => committer}, :include => :user)
196 200 if c && c.user
197 201 user = c.user
198 202 elsif committer.strip =~ /^([^<]+)(<(.*)>)?$/
199 203 username, email = $1.strip, $3
200 204 u = User.find_by_login(username)
201 205 u ||= User.find_by_mail(email) unless email.blank?
202 206 user = u
203 207 end
204 208 @found_committer_users[committer] = user
205 209 user
206 210 end
207 211 end
208 212
209 213 def repo_log_encoding
210 214 encoding = log_encoding.to_s.strip
211 215 encoding.blank? ? 'UTF-8' : encoding
212 216 end
213 217
214 218 # Fetches new changesets for all repositories of active projects
215 219 # Can be called periodically by an external script
216 220 # eg. ruby script/runner "Repository.fetch_changesets"
217 221 def self.fetch_changesets
218 222 Project.active.has_module(:repository).find(:all, :include => :repository).each do |project|
219 223 if project.repository
220 224 begin
221 225 project.repository.fetch_changesets
222 226 rescue Redmine::Scm::Adapters::CommandFailed => e
223 227 logger.error "scm: error during fetching changesets: #{e.message}"
224 228 end
225 229 end
226 230 end
227 231 end
228 232
229 233 # scan changeset comments to find related and fixed issues for all repositories
230 234 def self.scan_changesets_for_issue_ids
231 235 find(:all).each(&:scan_changesets_for_issue_ids)
232 236 end
233 237
234 238 def self.scm_name
235 239 'Abstract'
236 240 end
237 241
238 242 def self.available_scm
239 243 subclasses.collect {|klass| [klass.scm_name, klass.name]}
240 244 end
241 245
242 246 def self.factory(klass_name, *args)
243 247 klass = "Repository::#{klass_name}".constantize
244 248 klass.new(*args)
245 249 rescue
246 250 nil
247 251 end
248 252
249 253 def self.scm_adapter_class
250 254 nil
251 255 end
252 256
253 257 def self.scm_command
254 258 ret = ""
255 259 begin
256 260 ret = self.scm_adapter_class.client_command if self.scm_adapter_class
257 261 rescue Redmine::Scm::Adapters::CommandFailed => e
258 262 logger.error "scm: error during get command: #{e.message}"
259 263 end
260 264 ret
261 265 end
262 266
263 267 def self.scm_version_string
264 268 ret = ""
265 269 begin
266 270 ret = self.scm_adapter_class.client_version_string if self.scm_adapter_class
267 271 rescue Redmine::Scm::Adapters::CommandFailed => e
268 272 logger.error "scm: error during get version string: #{e.message}"
269 273 end
270 274 ret
271 275 end
272 276
273 277 def self.scm_available
274 278 ret = false
275 279 begin
276 280 ret = self.scm_adapter_class.client_available if self.scm_adapter_class
277 281 rescue Redmine::Scm::Adapters::CommandFailed => e
278 282 logger.error "scm: error during get scm available: #{e.message}"
279 283 end
280 284 ret
281 285 end
282 286
283 287 private
284 288
285 289 def before_save
286 290 # Strips url and root_url
287 291 url.strip!
288 292 root_url.strip!
289 293 true
290 294 end
291 295
292 296 def clear_changesets
293 297 cs, ch, ci = Changeset.table_name, Change.table_name, "#{table_name_prefix}changesets_issues#{table_name_suffix}"
294 298 connection.delete("DELETE FROM #{ch} WHERE #{ch}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
295 299 connection.delete("DELETE FROM #{ci} WHERE #{ci}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
296 300 connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}")
297 301 end
298 302 end
General Comments 0
You need to be logged in to leave comments. Login now