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