##// END OF EJS Templates
Eager-load users....
Jean-Philippe Lang -
r2007:e8d0c26e4393
parent child
Show More
@@ -1,179 +1,179
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, :activerecord_error_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 properties(path, identifier=nil)
67 67 scm.properties(path, identifier)
68 68 end
69 69
70 70 def cat(path, identifier=nil)
71 71 scm.cat(path, identifier)
72 72 end
73 73
74 74 def diff(path, rev, rev_to)
75 75 scm.diff(path, rev, rev_to)
76 76 end
77 77
78 78 # Default behaviour: we search in cached changesets
79 79 def changesets_for_path(path)
80 80 path = "/#{path}" unless path.starts_with?('/')
81 Change.find(:all, :include => :changeset,
81 Change.find(:all, :include => {:changeset => :user},
82 82 :conditions => ["repository_id = ? AND path = ?", id, path],
83 83 :order => "committed_on DESC, #{Changeset.table_name}.id DESC").collect(&:changeset)
84 84 end
85 85
86 86 # Returns a path relative to the url of the repository
87 87 def relative_path(path)
88 88 path
89 89 end
90 90
91 91 def latest_changeset
92 92 @latest_changeset ||= changesets.find(:first)
93 93 end
94 94
95 95 def scan_changesets_for_issue_ids
96 96 self.changesets.each(&:scan_comment_for_issue_ids)
97 97 end
98 98
99 99 # Returns an array of committers usernames and associated user_id
100 100 def committers
101 101 @committers ||= Changeset.connection.select_rows("SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}")
102 102 end
103 103
104 104 # Maps committers username to a user ids
105 105 def committer_ids=(h)
106 106 if h.is_a?(Hash)
107 107 committers.each do |committer, user_id|
108 108 new_user_id = h[committer]
109 109 if new_user_id && (new_user_id.to_i != user_id.to_i)
110 110 new_user_id = (new_user_id.to_i > 0 ? new_user_id.to_i : nil)
111 111 Changeset.update_all("user_id = #{ new_user_id.nil? ? 'NULL' : new_user_id }", ["repository_id = ? AND committer = ?", id, committer])
112 112 end
113 113 end
114 114 @committers = nil
115 115 true
116 116 else
117 117 false
118 118 end
119 119 end
120 120
121 121 # Returns the Redmine User corresponding to the given +committer+
122 122 # It will return nil if the committer is not yet mapped and if no User
123 123 # with the same username or email was found
124 124 def find_committer_user(committer)
125 125 if committer
126 126 c = changesets.find(:first, :conditions => {:committer => committer}, :include => :user)
127 127 if c && c.user
128 128 c.user
129 129 elsif committer.strip =~ /^([^<]+)(<(.*)>)?$/
130 130 username, email = $1.strip, $3
131 131 u = User.find_by_login(username)
132 132 u ||= User.find_by_mail(email) unless email.blank?
133 133 u
134 134 end
135 135 end
136 136 end
137 137
138 138 # fetch new changesets for all repositories
139 139 # can be called periodically by an external script
140 140 # eg. ruby script/runner "Repository.fetch_changesets"
141 141 def self.fetch_changesets
142 142 find(:all).each(&:fetch_changesets)
143 143 end
144 144
145 145 # scan changeset comments to find related and fixed issues for all repositories
146 146 def self.scan_changesets_for_issue_ids
147 147 find(:all).each(&:scan_changesets_for_issue_ids)
148 148 end
149 149
150 150 def self.scm_name
151 151 'Abstract'
152 152 end
153 153
154 154 def self.available_scm
155 155 subclasses.collect {|klass| [klass.scm_name, klass.name]}
156 156 end
157 157
158 158 def self.factory(klass_name, *args)
159 159 klass = "Repository::#{klass_name}".constantize
160 160 klass.new(*args)
161 161 rescue
162 162 nil
163 163 end
164 164
165 165 private
166 166
167 167 def before_save
168 168 # Strips url and root_url
169 169 url.strip!
170 170 root_url.strip!
171 171 true
172 172 end
173 173
174 174 def clear_changesets
175 175 connection.delete("DELETE FROM changes WHERE changes.changeset_id IN (SELECT changesets.id FROM changesets WHERE changesets.repository_id = #{id})")
176 176 connection.delete("DELETE FROM changesets_issues WHERE changesets_issues.changeset_id IN (SELECT changesets.id FROM changesets WHERE changesets.repository_id = #{id})")
177 177 connection.delete("DELETE FROM changesets WHERE changesets.repository_id = #{id}")
178 178 end
179 179 end
@@ -1,70 +1,70
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 # Copyright (C) 2007 Patrick Aljord patcito@Ε‹mail.com
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 require 'redmine/scm/adapters/git_adapter'
19 19
20 20 class Repository::Git < Repository
21 21 attr_protected :root_url
22 22 validates_presence_of :url
23 23
24 24 def scm_adapter
25 25 Redmine::Scm::Adapters::GitAdapter
26 26 end
27 27
28 28 def self.scm_name
29 29 'Git'
30 30 end
31 31
32 32 def changesets_for_path(path)
33 Change.find(:all, :include => :changeset,
33 Change.find(:all, :include => {:changeset => :user},
34 34 :conditions => ["repository_id = ? AND path = ?", id, path],
35 35 :order => "committed_on DESC, #{Changeset.table_name}.revision DESC").collect(&:changeset)
36 36 end
37 37
38 38 def fetch_changesets
39 39 scm_info = scm.info
40 40 if scm_info
41 41 # latest revision found in database
42 42 db_revision = latest_changeset ? latest_changeset.revision : nil
43 43 # latest revision in the repository
44 44 scm_revision = scm_info.lastrev.scmid
45 45
46 46 unless changesets.find_by_scmid(scm_revision)
47 47 scm.revisions('', db_revision, nil, :reverse => true) do |revision|
48 48 if changesets.find_by_scmid(revision.scmid.to_s).nil?
49 49 transaction do
50 50 changeset = Changeset.create!(:repository => self,
51 51 :revision => revision.identifier,
52 52 :scmid => revision.scmid,
53 53 :committer => revision.author,
54 54 :committed_on => revision.time,
55 55 :comments => revision.message)
56 56
57 57 revision.paths.each do |change|
58 58 Change.create!(:changeset => changeset,
59 59 :action => change[:action],
60 60 :path => change[:path],
61 61 :from_path => change[:from_path],
62 62 :from_revision => change[:from_revision])
63 63 end
64 64 end
65 65 end
66 66 end
67 67 end
68 68 end
69 69 end
70 70 end
@@ -1,89 +1,89
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 require 'redmine/scm/adapters/subversion_adapter'
19 19
20 20 class Repository::Subversion < Repository
21 21 attr_protected :root_url
22 22 validates_presence_of :url
23 23 validates_format_of :url, :with => /^(http|https|svn|svn\+ssh|file):\/\/.+/i
24 24
25 25 def scm_adapter
26 26 Redmine::Scm::Adapters::SubversionAdapter
27 27 end
28 28
29 29 def self.scm_name
30 30 'Subversion'
31 31 end
32 32
33 33 def changesets_for_path(path)
34 34 revisions = scm.revisions(path)
35 revisions ? changesets.find_all_by_revision(revisions.collect(&:identifier), :order => "committed_on DESC") : []
35 revisions ? changesets.find_all_by_revision(revisions.collect(&:identifier), :order => "committed_on DESC", :include => :user) : []
36 36 end
37 37
38 38 # Returns a path relative to the url of the repository
39 39 def relative_path(path)
40 40 path.gsub(Regexp.new("^\/?#{Regexp.escape(relative_url)}"), '')
41 41 end
42 42
43 43 def fetch_changesets
44 44 scm_info = scm.info
45 45 if scm_info
46 46 # latest revision found in database
47 47 db_revision = latest_changeset ? latest_changeset.revision.to_i : 0
48 48 # latest revision in the repository
49 49 scm_revision = scm_info.lastrev.identifier.to_i
50 50 if db_revision < scm_revision
51 51 logger.debug "Fetching changesets for repository #{url}" if logger && logger.debug?
52 52 identifier_from = db_revision + 1
53 53 while (identifier_from <= scm_revision)
54 54 # loads changesets by batches of 200
55 55 identifier_to = [identifier_from + 199, scm_revision].min
56 56 revisions = scm.revisions('', identifier_to, identifier_from, :with_paths => true)
57 57 transaction do
58 58 revisions.reverse_each do |revision|
59 59 changeset = Changeset.create(:repository => self,
60 60 :revision => revision.identifier,
61 61 :committer => revision.author,
62 62 :committed_on => revision.time,
63 63 :comments => revision.message)
64 64
65 65 revision.paths.each do |change|
66 66 Change.create(:changeset => changeset,
67 67 :action => change[:action],
68 68 :path => change[:path],
69 69 :from_path => change[:from_path],
70 70 :from_revision => change[:from_revision])
71 71 end
72 72 end
73 73 end unless revisions.nil?
74 74 identifier_from = identifier_to + 1
75 75 end
76 76 end
77 77 end
78 78 end
79 79
80 80 private
81 81
82 82 # Returns the relative url of the repository
83 83 # Eg: root_url = file:///var/svn/foo
84 84 # url = file:///var/svn/foo/bar
85 85 # => returns /bar
86 86 def relative_url
87 87 @relative_url ||= url.gsub(Regexp.new("^#{Regexp.escape(root_url)}"), '')
88 88 end
89 89 end
General Comments 0
You need to be logged in to leave comments. Login now