##// END OF EJS Templates
Fixed: Repository.fetch_changesets tries to fetch changesets for archived projects (#4782)....
Jean-Philippe Lang -
r3288:d04d3f181a7d
parent child
Show More
@@ -1,204 +1,208
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class Repository < ActiveRecord::Base
18 class Repository < ActiveRecord::Base
19 belongs_to :project
19 belongs_to :project
20 has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC"
20 has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC"
21 has_many :changes, :through => :changesets
21 has_many :changes, :through => :changesets
22
22
23 # Raw SQL to delete changesets and changes in the database
23 # Raw SQL to delete changesets and changes in the database
24 # has_many :changesets, :dependent => :destroy is too slow for big repositories
24 # has_many :changesets, :dependent => :destroy is too slow for big repositories
25 before_destroy :clear_changesets
25 before_destroy :clear_changesets
26
26
27 # Checks if the SCM is enabled when creating a repository
27 # Checks if the SCM is enabled when creating a repository
28 validate_on_create { |r| r.errors.add(:type, :invalid) unless Setting.enabled_scm.include?(r.class.name.demodulize) }
28 validate_on_create { |r| r.errors.add(:type, :invalid) unless Setting.enabled_scm.include?(r.class.name.demodulize) }
29
29
30 # Removes leading and trailing whitespace
30 # Removes leading and trailing whitespace
31 def url=(arg)
31 def url=(arg)
32 write_attribute(:url, arg ? arg.to_s.strip : nil)
32 write_attribute(:url, arg ? arg.to_s.strip : nil)
33 end
33 end
34
34
35 # Removes leading and trailing whitespace
35 # Removes leading and trailing whitespace
36 def root_url=(arg)
36 def root_url=(arg)
37 write_attribute(:root_url, arg ? arg.to_s.strip : nil)
37 write_attribute(:root_url, arg ? arg.to_s.strip : nil)
38 end
38 end
39
39
40 def scm
40 def scm
41 @scm ||= self.scm_adapter.new url, root_url, login, password
41 @scm ||= self.scm_adapter.new url, root_url, login, password
42 update_attribute(:root_url, @scm.root_url) if root_url.blank?
42 update_attribute(:root_url, @scm.root_url) if root_url.blank?
43 @scm
43 @scm
44 end
44 end
45
45
46 def scm_name
46 def scm_name
47 self.class.scm_name
47 self.class.scm_name
48 end
48 end
49
49
50 def supports_cat?
50 def supports_cat?
51 scm.supports_cat?
51 scm.supports_cat?
52 end
52 end
53
53
54 def supports_annotate?
54 def supports_annotate?
55 scm.supports_annotate?
55 scm.supports_annotate?
56 end
56 end
57
57
58 def entry(path=nil, identifier=nil)
58 def entry(path=nil, identifier=nil)
59 scm.entry(path, identifier)
59 scm.entry(path, identifier)
60 end
60 end
61
61
62 def entries(path=nil, identifier=nil)
62 def entries(path=nil, identifier=nil)
63 scm.entries(path, identifier)
63 scm.entries(path, identifier)
64 end
64 end
65
65
66 def branches
66 def branches
67 scm.branches
67 scm.branches
68 end
68 end
69
69
70 def tags
70 def tags
71 scm.tags
71 scm.tags
72 end
72 end
73
73
74 def default_branch
74 def default_branch
75 scm.default_branch
75 scm.default_branch
76 end
76 end
77
77
78 def properties(path, identifier=nil)
78 def properties(path, identifier=nil)
79 scm.properties(path, identifier)
79 scm.properties(path, identifier)
80 end
80 end
81
81
82 def cat(path, identifier=nil)
82 def cat(path, identifier=nil)
83 scm.cat(path, identifier)
83 scm.cat(path, identifier)
84 end
84 end
85
85
86 def diff(path, rev, rev_to)
86 def diff(path, rev, rev_to)
87 scm.diff(path, rev, rev_to)
87 scm.diff(path, rev, rev_to)
88 end
88 end
89
89
90 # Returns a path relative to the url of the repository
90 # Returns a path relative to the url of the repository
91 def relative_path(path)
91 def relative_path(path)
92 path
92 path
93 end
93 end
94
94
95 # Finds and returns a revision with a number or the beginning of a hash
95 # Finds and returns a revision with a number or the beginning of a hash
96 def find_changeset_by_name(name)
96 def find_changeset_by_name(name)
97 changesets.find(:first, :conditions => (name.match(/^\d*$/) ? ["revision = ?", name.to_s] : ["revision LIKE ?", name + '%']))
97 changesets.find(:first, :conditions => (name.match(/^\d*$/) ? ["revision = ?", name.to_s] : ["revision LIKE ?", name + '%']))
98 end
98 end
99
99
100 def latest_changeset
100 def latest_changeset
101 @latest_changeset ||= changesets.find(:first)
101 @latest_changeset ||= changesets.find(:first)
102 end
102 end
103
103
104 # Returns the latest changesets for +path+
104 # Returns the latest changesets for +path+
105 # Default behaviour is to search in cached changesets
105 # Default behaviour is to search in cached changesets
106 def latest_changesets(path, rev, limit=10)
106 def latest_changesets(path, rev, limit=10)
107 if path.blank?
107 if path.blank?
108 changesets.find(:all, :include => :user,
108 changesets.find(:all, :include => :user,
109 :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
109 :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
110 :limit => limit)
110 :limit => limit)
111 else
111 else
112 changes.find(:all, :include => {:changeset => :user},
112 changes.find(:all, :include => {:changeset => :user},
113 :conditions => ["path = ?", path.with_leading_slash],
113 :conditions => ["path = ?", path.with_leading_slash],
114 :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
114 :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
115 :limit => limit).collect(&:changeset)
115 :limit => limit).collect(&:changeset)
116 end
116 end
117 end
117 end
118
118
119 def scan_changesets_for_issue_ids
119 def scan_changesets_for_issue_ids
120 self.changesets.each(&:scan_comment_for_issue_ids)
120 self.changesets.each(&:scan_comment_for_issue_ids)
121 end
121 end
122
122
123 # Returns an array of committers usernames and associated user_id
123 # Returns an array of committers usernames and associated user_id
124 def committers
124 def committers
125 @committers ||= Changeset.connection.select_rows("SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}")
125 @committers ||= Changeset.connection.select_rows("SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}")
126 end
126 end
127
127
128 # Maps committers username to a user ids
128 # Maps committers username to a user ids
129 def committer_ids=(h)
129 def committer_ids=(h)
130 if h.is_a?(Hash)
130 if h.is_a?(Hash)
131 committers.each do |committer, user_id|
131 committers.each do |committer, user_id|
132 new_user_id = h[committer]
132 new_user_id = h[committer]
133 if new_user_id && (new_user_id.to_i != user_id.to_i)
133 if new_user_id && (new_user_id.to_i != user_id.to_i)
134 new_user_id = (new_user_id.to_i > 0 ? new_user_id.to_i : nil)
134 new_user_id = (new_user_id.to_i > 0 ? new_user_id.to_i : nil)
135 Changeset.update_all("user_id = #{ new_user_id.nil? ? 'NULL' : new_user_id }", ["repository_id = ? AND committer = ?", id, committer])
135 Changeset.update_all("user_id = #{ new_user_id.nil? ? 'NULL' : new_user_id }", ["repository_id = ? AND committer = ?", id, committer])
136 end
136 end
137 end
137 end
138 @committers = nil
138 @committers = nil
139 true
139 true
140 else
140 else
141 false
141 false
142 end
142 end
143 end
143 end
144
144
145 # Returns the Redmine User corresponding to the given +committer+
145 # Returns the Redmine User corresponding to the given +committer+
146 # It will return nil if the committer is not yet mapped and if no User
146 # It will return nil if the committer is not yet mapped and if no User
147 # with the same username or email was found
147 # with the same username or email was found
148 def find_committer_user(committer)
148 def find_committer_user(committer)
149 if committer
149 if committer
150 c = changesets.find(:first, :conditions => {:committer => committer}, :include => :user)
150 c = changesets.find(:first, :conditions => {:committer => committer}, :include => :user)
151 if c && c.user
151 if c && c.user
152 c.user
152 c.user
153 elsif committer.strip =~ /^([^<]+)(<(.*)>)?$/
153 elsif committer.strip =~ /^([^<]+)(<(.*)>)?$/
154 username, email = $1.strip, $3
154 username, email = $1.strip, $3
155 u = User.find_by_login(username)
155 u = User.find_by_login(username)
156 u ||= User.find_by_mail(email) unless email.blank?
156 u ||= User.find_by_mail(email) unless email.blank?
157 u
157 u
158 end
158 end
159 end
159 end
160 end
160 end
161
161
162 # fetch new changesets for all repositories
162 # Fetches new changesets for all repositories of active projects
163 # can be called periodically by an external script
163 # Can be called periodically by an external script
164 # eg. ruby script/runner "Repository.fetch_changesets"
164 # eg. ruby script/runner "Repository.fetch_changesets"
165 def self.fetch_changesets
165 def self.fetch_changesets
166 find(:all).each(&:fetch_changesets)
166 Project.active.has_module(:repository).find(:all, :include => :repository).each do |project|
167 if project.repository
168 project.repository.fetch_changesets
169 end
170 end
167 end
171 end
168
172
169 # scan changeset comments to find related and fixed issues for all repositories
173 # scan changeset comments to find related and fixed issues for all repositories
170 def self.scan_changesets_for_issue_ids
174 def self.scan_changesets_for_issue_ids
171 find(:all).each(&:scan_changesets_for_issue_ids)
175 find(:all).each(&:scan_changesets_for_issue_ids)
172 end
176 end
173
177
174 def self.scm_name
178 def self.scm_name
175 'Abstract'
179 'Abstract'
176 end
180 end
177
181
178 def self.available_scm
182 def self.available_scm
179 subclasses.collect {|klass| [klass.scm_name, klass.name]}
183 subclasses.collect {|klass| [klass.scm_name, klass.name]}
180 end
184 end
181
185
182 def self.factory(klass_name, *args)
186 def self.factory(klass_name, *args)
183 klass = "Repository::#{klass_name}".constantize
187 klass = "Repository::#{klass_name}".constantize
184 klass.new(*args)
188 klass.new(*args)
185 rescue
189 rescue
186 nil
190 nil
187 end
191 end
188
192
189 private
193 private
190
194
191 def before_save
195 def before_save
192 # Strips url and root_url
196 # Strips url and root_url
193 url.strip!
197 url.strip!
194 root_url.strip!
198 root_url.strip!
195 true
199 true
196 end
200 end
197
201
198 def clear_changesets
202 def clear_changesets
199 cs, ch, ci = Changeset.table_name, Change.table_name, "#{table_name_prefix}changesets_issues#{table_name_suffix}"
203 cs, ch, ci = Changeset.table_name, Change.table_name, "#{table_name_prefix}changesets_issues#{table_name_suffix}"
200 connection.delete("DELETE FROM #{ch} WHERE #{ch}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
204 connection.delete("DELETE FROM #{ch} WHERE #{ch}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
201 connection.delete("DELETE FROM #{ci} WHERE #{ci}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
205 connection.delete("DELETE FROM #{ci} WHERE #{ci}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
202 connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}")
206 connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}")
203 end
207 end
204 end
208 end
General Comments 0
You need to be logged in to leave comments. Login now