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