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