##// END OF EJS Templates
Memorize commit authors to speed up changesets loading....
Jean-Philippe Lang -
r3358:3d393a5711f6
parent child
Show More
@@ -1,208 +1,215
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
41 41 @scm ||= self.scm_adapter.new url, root_url, login, password
42 42 update_attribute(:root_url, @scm.root_url) if root_url.blank?
43 43 @scm
44 44 end
45 45
46 46 def scm_name
47 47 self.class.scm_name
48 48 end
49 49
50 50 def supports_cat?
51 51 scm.supports_cat?
52 52 end
53 53
54 54 def supports_annotate?
55 55 scm.supports_annotate?
56 56 end
57 57
58 58 def entry(path=nil, identifier=nil)
59 59 scm.entry(path, identifier)
60 60 end
61 61
62 62 def entries(path=nil, identifier=nil)
63 63 scm.entries(path, identifier)
64 64 end
65 65
66 66 def branches
67 67 scm.branches
68 68 end
69 69
70 70 def tags
71 71 scm.tags
72 72 end
73 73
74 74 def default_branch
75 75 scm.default_branch
76 76 end
77 77
78 78 def properties(path, identifier=nil)
79 79 scm.properties(path, identifier)
80 80 end
81 81
82 82 def cat(path, identifier=nil)
83 83 scm.cat(path, identifier)
84 84 end
85 85
86 86 def diff(path, rev, rev_to)
87 87 scm.diff(path, rev, rev_to)
88 88 end
89 89
90 90 # Returns a path relative to the url of the repository
91 91 def relative_path(path)
92 92 path
93 93 end
94 94
95 95 # Finds and returns a revision with a number or the beginning of a hash
96 96 def find_changeset_by_name(name)
97 97 changesets.find(:first, :conditions => (name.match(/^\d*$/) ? ["revision = ?", name.to_s] : ["revision LIKE ?", name + '%']))
98 98 end
99 99
100 100 def latest_changeset
101 101 @latest_changeset ||= changesets.find(:first)
102 102 end
103 103
104 104 # Returns the latest changesets for +path+
105 105 # Default behaviour is to search in cached changesets
106 106 def latest_changesets(path, rev, limit=10)
107 107 if path.blank?
108 108 changesets.find(:all, :include => :user,
109 109 :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
110 110 :limit => limit)
111 111 else
112 112 changes.find(:all, :include => {:changeset => :user},
113 113 :conditions => ["path = ?", path.with_leading_slash],
114 114 :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
115 115 :limit => limit).collect(&:changeset)
116 116 end
117 117 end
118 118
119 119 def scan_changesets_for_issue_ids
120 120 self.changesets.each(&:scan_comment_for_issue_ids)
121 121 end
122 122
123 123 # Returns an array of committers usernames and associated user_id
124 124 def committers
125 125 @committers ||= Changeset.connection.select_rows("SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}")
126 126 end
127 127
128 128 # Maps committers username to a user ids
129 129 def committer_ids=(h)
130 130 if h.is_a?(Hash)
131 131 committers.each do |committer, user_id|
132 132 new_user_id = h[committer]
133 133 if new_user_id && (new_user_id.to_i != user_id.to_i)
134 134 new_user_id = (new_user_id.to_i > 0 ? new_user_id.to_i : nil)
135 135 Changeset.update_all("user_id = #{ new_user_id.nil? ? 'NULL' : new_user_id }", ["repository_id = ? AND committer = ?", id, committer])
136 136 end
137 137 end
138 138 @committers = nil
139 @found_committer_users = nil
139 140 true
140 141 else
141 142 false
142 143 end
143 144 end
144 145
145 146 # Returns the Redmine User corresponding to the given +committer+
146 147 # It will return nil if the committer is not yet mapped and if no User
147 148 # with the same username or email was found
148 149 def find_committer_user(committer)
149 if committer
150 unless committer.blank?
151 @found_committer_users ||= {}
152 return @found_committer_users[committer] if @found_committer_users.has_key?(committer)
153
154 user = nil
150 155 c = changesets.find(:first, :conditions => {:committer => committer}, :include => :user)
151 156 if c && c.user
152 c.user
157 user = c.user
153 158 elsif committer.strip =~ /^([^<]+)(<(.*)>)?$/
154 159 username, email = $1.strip, $3
155 160 u = User.find_by_login(username)
156 161 u ||= User.find_by_mail(email) unless email.blank?
157 u
162 user = u
158 163 end
164 @found_committer_users[committer] = user
165 user
159 166 end
160 167 end
161 168
162 169 # Fetches new changesets for all repositories of active projects
163 170 # Can be called periodically by an external script
164 171 # eg. ruby script/runner "Repository.fetch_changesets"
165 172 def self.fetch_changesets
166 173 Project.active.has_module(:repository).find(:all, :include => :repository).each do |project|
167 174 if project.repository
168 175 project.repository.fetch_changesets
169 176 end
170 177 end
171 178 end
172 179
173 180 # scan changeset comments to find related and fixed issues for all repositories
174 181 def self.scan_changesets_for_issue_ids
175 182 find(:all).each(&:scan_changesets_for_issue_ids)
176 183 end
177 184
178 185 def self.scm_name
179 186 'Abstract'
180 187 end
181 188
182 189 def self.available_scm
183 190 subclasses.collect {|klass| [klass.scm_name, klass.name]}
184 191 end
185 192
186 193 def self.factory(klass_name, *args)
187 194 klass = "Repository::#{klass_name}".constantize
188 195 klass.new(*args)
189 196 rescue
190 197 nil
191 198 end
192 199
193 200 private
194 201
195 202 def before_save
196 203 # Strips url and root_url
197 204 url.strip!
198 205 root_url.strip!
199 206 true
200 207 end
201 208
202 209 def clear_changesets
203 210 cs, ch, ci = Changeset.table_name, Change.table_name, "#{table_name_prefix}changesets_issues#{table_name_suffix}"
204 211 connection.delete("DELETE FROM #{ch} WHERE #{ch}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
205 212 connection.delete("DELETE FROM #{ci} WHERE #{ci}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
206 213 connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}")
207 214 end
208 215 end
General Comments 0
You need to be logged in to leave comments. Login now