##// END OF EJS Templates
Use exists? instead of count (#24839)....
Jean-Philippe Lang -
r15829:c7f03c00a922
parent child
Show More
@@ -1,265 +1,265
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 # Copyright (C) 2007 Patrick Aljord patcito@ŋmail.com
4 4 #
5 5 # This program is free software; you can redistribute it and/or
6 6 # modify it under the terms of the GNU General Public License
7 7 # as published by the Free Software Foundation; either version 2
8 8 # of the License, or (at your option) any later version.
9 9 #
10 10 # This program is distributed in the hope that it will be useful,
11 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 13 # GNU General Public License for more details.
14 14 #
15 15 # You should have received a copy of the GNU General Public License
16 16 # along with this program; if not, write to the Free Software
17 17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 18
19 19 require 'redmine/scm/adapters/git_adapter'
20 20
21 21 class Repository::Git < Repository
22 22 attr_protected :root_url
23 23 validates_presence_of :url
24 24
25 25 safe_attributes 'report_last_commit'
26 26
27 27 def self.human_attribute_name(attribute_key_name, *args)
28 28 attr_name = attribute_key_name.to_s
29 29 if attr_name == "url"
30 30 attr_name = "path_to_repository"
31 31 end
32 32 super(attr_name, *args)
33 33 end
34 34
35 35 def self.scm_adapter_class
36 36 Redmine::Scm::Adapters::GitAdapter
37 37 end
38 38
39 39 def self.scm_name
40 40 'Git'
41 41 end
42 42
43 43 def report_last_commit
44 44 return false if extra_info.nil?
45 45 v = extra_info["extra_report_last_commit"]
46 46 return false if v.nil?
47 47 v.to_s != '0'
48 48 end
49 49
50 50 def report_last_commit=(arg)
51 51 merge_extra_info "extra_report_last_commit" => arg
52 52 end
53 53
54 54 def supports_directory_revisions?
55 55 true
56 56 end
57 57
58 58 def supports_revision_graph?
59 59 true
60 60 end
61 61
62 62 def repo_log_encoding
63 63 'UTF-8'
64 64 end
65 65
66 66 # Returns the identifier for the given git changeset
67 67 def self.changeset_identifier(changeset)
68 68 changeset.scmid
69 69 end
70 70
71 71 # Returns the readable identifier for the given git changeset
72 72 def self.format_changeset_identifier(changeset)
73 73 changeset.revision[0, 8]
74 74 end
75 75
76 76 def branches
77 77 scm.branches
78 78 end
79 79
80 80 def tags
81 81 scm.tags
82 82 end
83 83
84 84 def default_branch
85 85 scm.default_branch
86 86 rescue Exception => e
87 87 logger.error "git: error during get default branch: #{e.message}"
88 88 nil
89 89 end
90 90
91 91 def find_changeset_by_name(name)
92 92 if name.present?
93 93 changesets.where(:revision => name.to_s).first ||
94 94 changesets.where('scmid LIKE ?', "#{name}%").first
95 95 end
96 96 end
97 97
98 98 def scm_entries(path=nil, identifier=nil)
99 99 scm.entries(path, identifier, :report_last_commit => report_last_commit)
100 100 end
101 101 protected :scm_entries
102 102
103 103 # With SCMs that have a sequential commit numbering,
104 104 # such as Subversion and Mercurial,
105 105 # Redmine is able to be clever and only fetch changesets
106 106 # going forward from the most recent one it knows about.
107 107 #
108 108 # However, Git does not have a sequential commit numbering.
109 109 #
110 110 # In order to fetch only new adding revisions,
111 111 # Redmine needs to save "heads".
112 112 #
113 113 # In Git and Mercurial, revisions are not in date order.
114 114 # Redmine Mercurial fixed issues.
115 115 # * Redmine Takes Too Long On Large Mercurial Repository
116 116 # http://www.redmine.org/issues/3449
117 117 # * Sorting for changesets might go wrong on Mercurial repos
118 118 # http://www.redmine.org/issues/3567
119 119 #
120 120 # Database revision column is text, so Redmine can not sort by revision.
121 121 # Mercurial has revision number, and revision number guarantees revision order.
122 122 # Redmine Mercurial model stored revisions ordered by database id to database.
123 123 # So, Redmine Mercurial model can use correct ordering revisions.
124 124 #
125 125 # Redmine Mercurial adapter uses "hg log -r 0:tip --limit 10"
126 126 # to get limited revisions from old to new.
127 127 # But, Git 1.7.3.4 does not support --reverse with -n or --skip.
128 128 #
129 129 # The repository can still be fully reloaded by calling #clear_changesets
130 130 # before fetching changesets (eg. for offline resync)
131 131 def fetch_changesets
132 132 scm_brs = branches
133 133 return if scm_brs.nil? || scm_brs.empty?
134 134
135 135 h1 = extra_info || {}
136 136 h = h1.dup
137 137 repo_heads = scm_brs.map{ |br| br.scmid }
138 138 h["heads"] ||= []
139 139 prev_db_heads = h["heads"].dup
140 140 if prev_db_heads.empty?
141 141 prev_db_heads += heads_from_branches_hash
142 142 end
143 143 return if prev_db_heads.sort == repo_heads.sort
144 144
145 145 h["db_consistent"] ||= {}
146 if changesets.count == 0
146 if ! changesets.exists?
147 147 h["db_consistent"]["ordering"] = 1
148 148 merge_extra_info(h)
149 149 self.save
150 150 elsif ! h["db_consistent"].has_key?("ordering")
151 151 h["db_consistent"]["ordering"] = 0
152 152 merge_extra_info(h)
153 153 self.save
154 154 end
155 155 save_revisions(prev_db_heads, repo_heads)
156 156 end
157 157
158 158 def save_revisions(prev_db_heads, repo_heads)
159 159 h = {}
160 160 opts = {}
161 161 opts[:reverse] = true
162 162 opts[:excludes] = prev_db_heads
163 163 opts[:includes] = repo_heads
164 164
165 165 revisions = scm.revisions('', nil, nil, opts)
166 166 return if revisions.blank?
167 167
168 168 # Make the search for existing revisions in the database in a more sufficient manner
169 169 #
170 170 # Git branch is the reference to the specific revision.
171 171 # Git can *delete* remote branch and *re-push* branch.
172 172 #
173 173 # $ git push remote :branch
174 174 # $ git push remote branch
175 175 #
176 176 # After deleting branch, revisions remain in repository until "git gc".
177 177 # On git 1.7.2.3, default pruning date is 2 weeks.
178 178 # So, "git log --not deleted_branch_head_revision" return code is 0.
179 179 #
180 180 # After re-pushing branch, "git log" returns revisions which are saved in database.
181 181 # So, Redmine needs to scan revisions and database every time.
182 182 #
183 183 # This is replacing the one-after-one queries.
184 184 # Find all revisions, that are in the database, and then remove them
185 185 # from the revision array.
186 186 # Then later we won't need any conditions for db existence.
187 187 # Query for several revisions at once, and remove them
188 188 # from the revisions array, if they are there.
189 189 # Do this in chunks, to avoid eventual memory problems
190 190 # (in case of tens of thousands of commits).
191 191 # If there are no revisions (because the original code's algorithm filtered them),
192 192 # then this part will be stepped over.
193 193 # We make queries, just if there is any revision.
194 194 limit = 100
195 195 offset = 0
196 196 revisions_copy = revisions.clone # revisions will change
197 197 while offset < revisions_copy.size
198 198 scmids = revisions_copy.slice(offset, limit).map{|x| x.scmid}
199 199 recent_changesets_slice = changesets.where(:scmid => scmids)
200 200 # Subtract revisions that redmine already knows about
201 201 recent_revisions = recent_changesets_slice.map{|c| c.scmid}
202 202 revisions.reject!{|r| recent_revisions.include?(r.scmid)}
203 203 offset += limit
204 204 end
205 205 revisions.each do |rev|
206 206 transaction do
207 207 # There is no search in the db for this revision, because above we ensured,
208 208 # that it's not in the db.
209 209 save_revision(rev)
210 210 end
211 211 end
212 212 h["heads"] = repo_heads.dup
213 213 merge_extra_info(h)
214 214 save(:validate => false)
215 215 end
216 216 private :save_revisions
217 217
218 218 def save_revision(rev)
219 219 parents = (rev.parents || []).collect{|rp| find_changeset_by_name(rp)}.compact
220 220 changeset = Changeset.create(
221 221 :repository => self,
222 222 :revision => rev.identifier,
223 223 :scmid => rev.scmid,
224 224 :committer => rev.author,
225 225 :committed_on => rev.time,
226 226 :comments => rev.message,
227 227 :parents => parents
228 228 )
229 229 unless changeset.new_record?
230 230 rev.paths.each { |change| changeset.create_change(change) }
231 231 end
232 232 changeset
233 233 end
234 234 private :save_revision
235 235
236 236 def heads_from_branches_hash
237 237 h1 = extra_info || {}
238 238 h = h1.dup
239 239 h["branches"] ||= {}
240 240 h['branches'].map{|br, hs| hs['last_scmid']}
241 241 end
242 242
243 243 def latest_changesets(path,rev,limit=10)
244 244 revisions = scm.revisions(path, nil, rev, :limit => limit, :all => false)
245 245 return [] if revisions.nil? || revisions.empty?
246 246 changesets.where(:scmid => revisions.map {|c| c.scmid}).to_a
247 247 end
248 248
249 249 def clear_extra_info_of_changesets
250 250 return if extra_info.nil?
251 251 v = extra_info["extra_report_last_commit"]
252 252 write_attribute(:extra_info, nil)
253 253 h = {}
254 254 h["extra_report_last_commit"] = v
255 255 merge_extra_info(h)
256 256 save(:validate => false)
257 257 end
258 258 private :clear_extra_info_of_changesets
259 259
260 260 def clear_changesets
261 261 super
262 262 clear_extra_info_of_changesets
263 263 end
264 264 private :clear_changesets
265 265 end
@@ -1,38 +1,38
1 1 <div class="contextual">
2 2 <%= link_to l(:label_tracker_new), new_tracker_path, :class => 'icon icon-add' %>
3 3 <%= link_to l(:field_summary), fields_trackers_path, :class => 'icon icon-summary' %>
4 4 </div>
5 5
6 6 <h2><%=l(:label_tracker_plural)%></h2>
7 7
8 8 <table class="list trackers">
9 9 <thead><tr>
10 10 <th><%=l(:label_tracker)%></th>
11 11 <th></th>
12 12 <th></th>
13 13 </tr></thead>
14 14 <tbody>
15 15 <% for tracker in @trackers %>
16 16 <tr class="<%= cycle("odd", "even") %>">
17 17 <td class="name"><%= link_to tracker.name, edit_tracker_path(tracker) %></td>
18 18 <td>
19 <% unless tracker.workflow_rules.count > 0 %>
19 <% unless tracker.workflow_rules.exists? %>
20 20 <span class="icon icon-warning">
21 21 <%= l(:text_tracker_no_workflow) %> (<%= link_to l(:button_edit), workflows_edit_path(:tracker_id => tracker) %>)
22 22 </span>
23 23 <% end %>
24 24 </td>
25 25 <td class="buttons">
26 26 <%= reorder_handle(tracker) %>
27 27 <%= delete_link tracker_path(tracker) %>
28 28 </td>
29 29 </tr>
30 30 <% end %>
31 31 </tbody>
32 32 </table>
33 33
34 34 <% html_title(l(:label_tracker_plural)) -%>
35 35
36 36 <%= javascript_tag do %>
37 37 $(function() { $("table.trackers tbody").positionedItems(); });
38 38 <% end %>
General Comments 0
You need to be logged in to leave comments. Login now