##// END OF EJS Templates
Merged r10856 and r10857 from trunk to 2.1-stable (#12409)...
Toshi MARUYAMA -
r10631:06b68f194811
parent child
Show More
@@ -1,431 +1,435
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2012 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 ScmFetchError < Exception; end
19 19
20 20 class Repository < ActiveRecord::Base
21 21 include Redmine::Ciphering
22 22 include Redmine::SafeAttributes
23 23
24 24 # Maximum length for repository identifiers
25 25 IDENTIFIER_MAX_LENGTH = 255
26 26
27 27 belongs_to :project
28 28 has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC"
29 29 has_many :filechanges, :class_name => 'Change', :through => :changesets
30 30
31 31 serialize :extra_info
32 32
33 33 before_save :check_default
34 34
35 35 # Raw SQL to delete changesets and changes in the database
36 36 # has_many :changesets, :dependent => :destroy is too slow for big repositories
37 37 before_destroy :clear_changesets
38 38
39 39 validates_length_of :password, :maximum => 255, :allow_nil => true
40 40 validates_length_of :identifier, :maximum => IDENTIFIER_MAX_LENGTH, :allow_blank => true
41 41 validates_presence_of :identifier, :unless => Proc.new { |r| r.is_default? || r.set_as_default? }
42 42 validates_uniqueness_of :identifier, :scope => :project_id, :allow_blank => true
43 43 validates_exclusion_of :identifier, :in => %w(show entry raw changes annotate diff show stats graph)
44 44 # donwcase letters, digits, dashes, underscores but not digits only
45 45 validates_format_of :identifier, :with => /^(?!\d+$)[a-z0-9\-_]*$/, :allow_blank => true
46 46 # Checks if the SCM is enabled when creating a repository
47 47 validate :repo_create_validation, :on => :create
48 48
49 49 safe_attributes 'identifier',
50 50 'login',
51 51 'password',
52 52 'path_encoding',
53 53 'log_encoding',
54 54 'is_default'
55 55
56 56 safe_attributes 'url',
57 57 :if => lambda {|repository, user| repository.new_record?}
58 58
59 59 def repo_create_validation
60 60 unless Setting.enabled_scm.include?(self.class.name.demodulize)
61 61 errors.add(:type, :invalid)
62 62 end
63 63 end
64 64
65 65 def self.human_attribute_name(attribute_key_name, *args)
66 66 attr_name = attribute_key_name.to_s
67 67 if attr_name == "log_encoding"
68 68 attr_name = "commit_logs_encoding"
69 69 end
70 70 super(attr_name, *args)
71 71 end
72 72
73 73 # Removes leading and trailing whitespace
74 74 def url=(arg)
75 75 write_attribute(:url, arg ? arg.to_s.strip : nil)
76 76 end
77 77
78 78 # Removes leading and trailing whitespace
79 79 def root_url=(arg)
80 80 write_attribute(:root_url, arg ? arg.to_s.strip : nil)
81 81 end
82 82
83 83 def password
84 84 read_ciphered_attribute(:password)
85 85 end
86 86
87 87 def password=(arg)
88 88 write_ciphered_attribute(:password, arg)
89 89 end
90 90
91 91 def scm_adapter
92 92 self.class.scm_adapter_class
93 93 end
94 94
95 95 def scm
96 96 unless @scm
97 97 @scm = self.scm_adapter.new(url, root_url,
98 98 login, password, path_encoding)
99 99 if root_url.blank? && @scm.root_url.present?
100 100 update_attribute(:root_url, @scm.root_url)
101 101 end
102 102 end
103 103 @scm
104 104 end
105 105
106 106 def scm_name
107 107 self.class.scm_name
108 108 end
109 109
110 110 def name
111 111 if identifier.present?
112 112 identifier
113 113 elsif is_default?
114 114 l(:field_repository_is_default)
115 115 else
116 116 scm_name
117 117 end
118 118 end
119 119
120 120 def identifier=(identifier)
121 121 super unless identifier_frozen?
122 122 end
123 123
124 124 def identifier_frozen?
125 125 errors[:identifier].blank? && !(new_record? || identifier.blank?)
126 126 end
127 127
128 128 def identifier_param
129 129 if is_default?
130 130 nil
131 131 elsif identifier.present?
132 132 identifier
133 133 else
134 134 id.to_s
135 135 end
136 136 end
137 137
138 138 def <=>(repository)
139 139 if is_default?
140 140 -1
141 141 elsif repository.is_default?
142 142 1
143 143 else
144 144 identifier.to_s <=> repository.identifier.to_s
145 145 end
146 146 end
147 147
148 148 def self.find_by_identifier_param(param)
149 149 if param.to_s =~ /^\d+$/
150 150 find_by_id(param)
151 151 else
152 152 find_by_identifier(param)
153 153 end
154 154 end
155 155
156 156 def merge_extra_info(arg)
157 157 h = extra_info || {}
158 158 return h if arg.nil?
159 159 h.merge!(arg)
160 160 write_attribute(:extra_info, h)
161 161 end
162 162
163 163 def report_last_commit
164 164 true
165 165 end
166 166
167 167 def supports_cat?
168 168 scm.supports_cat?
169 169 end
170 170
171 171 def supports_annotate?
172 172 scm.supports_annotate?
173 173 end
174 174
175 175 def supports_all_revisions?
176 176 true
177 177 end
178 178
179 179 def supports_directory_revisions?
180 180 false
181 181 end
182 182
183 183 def supports_revision_graph?
184 184 false
185 185 end
186 186
187 187 def entry(path=nil, identifier=nil)
188 188 scm.entry(path, identifier)
189 189 end
190 190
191 191 def entries(path=nil, identifier=nil)
192 192 entries = scm.entries(path, identifier)
193 193 load_entries_changesets(entries)
194 194 entries
195 195 end
196 196
197 197 def branches
198 198 scm.branches
199 199 end
200 200
201 201 def tags
202 202 scm.tags
203 203 end
204 204
205 205 def default_branch
206 206 nil
207 207 end
208 208
209 209 def properties(path, identifier=nil)
210 210 scm.properties(path, identifier)
211 211 end
212 212
213 213 def cat(path, identifier=nil)
214 214 scm.cat(path, identifier)
215 215 end
216 216
217 217 def diff(path, rev, rev_to)
218 218 scm.diff(path, rev, rev_to)
219 219 end
220 220
221 221 def diff_format_revisions(cs, cs_to, sep=':')
222 222 text = ""
223 223 text << cs_to.format_identifier + sep if cs_to
224 224 text << cs.format_identifier if cs
225 225 text
226 226 end
227 227
228 228 # Returns a path relative to the url of the repository
229 229 def relative_path(path)
230 230 path
231 231 end
232 232
233 233 # Finds and returns a revision with a number or the beginning of a hash
234 234 def find_changeset_by_name(name)
235 235 return nil if name.blank?
236 236 s = name.to_s
237 237 changesets.find(:first, :conditions => (s.match(/^\d*$/) ?
238 238 ["revision = ?", s] : ["revision LIKE ?", s + '%']))
239 239 end
240 240
241 241 def latest_changeset
242 242 @latest_changeset ||= changesets.find(:first)
243 243 end
244 244
245 245 # Returns the latest changesets for +path+
246 246 # Default behaviour is to search in cached changesets
247 247 def latest_changesets(path, rev, limit=10)
248 248 if path.blank?
249 249 changesets.find(
250 250 :all,
251 251 :include => :user,
252 252 :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
253 253 :limit => limit)
254 254 else
255 255 filechanges.find(
256 256 :all,
257 257 :include => {:changeset => :user},
258 258 :conditions => ["path = ?", path.with_leading_slash],
259 259 :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
260 260 :limit => limit
261 261 ).collect(&:changeset)
262 262 end
263 263 end
264 264
265 265 def scan_changesets_for_issue_ids
266 266 self.changesets.each(&:scan_comment_for_issue_ids)
267 267 end
268 268
269 269 # Returns an array of committers usernames and associated user_id
270 270 def committers
271 271 @committers ||= Changeset.connection.select_rows(
272 272 "SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}")
273 273 end
274 274
275 275 # Maps committers username to a user ids
276 276 def committer_ids=(h)
277 277 if h.is_a?(Hash)
278 278 committers.each do |committer, user_id|
279 279 new_user_id = h[committer]
280 280 if new_user_id && (new_user_id.to_i != user_id.to_i)
281 281 new_user_id = (new_user_id.to_i > 0 ? new_user_id.to_i : nil)
282 282 Changeset.update_all(
283 283 "user_id = #{ new_user_id.nil? ? 'NULL' : new_user_id }",
284 284 ["repository_id = ? AND committer = ?", id, committer])
285 285 end
286 286 end
287 287 @committers = nil
288 288 @found_committer_users = nil
289 289 true
290 290 else
291 291 false
292 292 end
293 293 end
294 294
295 295 # Returns the Redmine User corresponding to the given +committer+
296 296 # It will return nil if the committer is not yet mapped and if no User
297 297 # with the same username or email was found
298 298 def find_committer_user(committer)
299 299 unless committer.blank?
300 300 @found_committer_users ||= {}
301 301 return @found_committer_users[committer] if @found_committer_users.has_key?(committer)
302 302
303 303 user = nil
304 304 c = changesets.find(:first, :conditions => {:committer => committer}, :include => :user)
305 305 if c && c.user
306 306 user = c.user
307 307 elsif committer.strip =~ /^([^<]+)(<(.*)>)?$/
308 308 username, email = $1.strip, $3
309 309 u = User.find_by_login(username)
310 310 u ||= User.find_by_mail(email) unless email.blank?
311 311 user = u
312 312 end
313 313 @found_committer_users[committer] = user
314 314 user
315 315 end
316 316 end
317 317
318 318 def repo_log_encoding
319 319 encoding = log_encoding.to_s.strip
320 320 encoding.blank? ? 'UTF-8' : encoding
321 321 end
322 322
323 323 # Fetches new changesets for all repositories of active projects
324 324 # Can be called periodically by an external script
325 325 # eg. ruby script/runner "Repository.fetch_changesets"
326 326 def self.fetch_changesets
327 327 Project.active.has_module(:repository).all.each do |project|
328 328 project.repositories.each do |repository|
329 329 begin
330 330 repository.fetch_changesets
331 331 rescue Redmine::Scm::Adapters::CommandFailed => e
332 332 logger.error "scm: error during fetching changesets: #{e.message}"
333 333 end
334 334 end
335 335 end
336 336 end
337 337
338 338 # scan changeset comments to find related and fixed issues for all repositories
339 339 def self.scan_changesets_for_issue_ids
340 340 find(:all).each(&:scan_changesets_for_issue_ids)
341 341 end
342 342
343 343 def self.scm_name
344 344 'Abstract'
345 345 end
346 346
347 347 def self.available_scm
348 348 subclasses.collect {|klass| [klass.scm_name, klass.name]}
349 349 end
350 350
351 351 def self.factory(klass_name, *args)
352 352 klass = "Repository::#{klass_name}".constantize
353 353 klass.new(*args)
354 354 rescue
355 355 nil
356 356 end
357 357
358 358 def self.scm_adapter_class
359 359 nil
360 360 end
361 361
362 362 def self.scm_command
363 363 ret = ""
364 364 begin
365 365 ret = self.scm_adapter_class.client_command if self.scm_adapter_class
366 366 rescue Exception => e
367 367 logger.error "scm: error during get command: #{e.message}"
368 368 end
369 369 ret
370 370 end
371 371
372 372 def self.scm_version_string
373 373 ret = ""
374 374 begin
375 375 ret = self.scm_adapter_class.client_version_string if self.scm_adapter_class
376 376 rescue Exception => e
377 377 logger.error "scm: error during get version string: #{e.message}"
378 378 end
379 379 ret
380 380 end
381 381
382 382 def self.scm_available
383 383 ret = false
384 384 begin
385 385 ret = self.scm_adapter_class.client_available if self.scm_adapter_class
386 386 rescue Exception => e
387 387 logger.error "scm: error during get scm available: #{e.message}"
388 388 end
389 389 ret
390 390 end
391 391
392 392 def set_as_default?
393 393 new_record? && project && !Repository.first(:conditions => {:project_id => project.id})
394 394 end
395 395
396 396 protected
397 397
398 398 def check_default
399 399 if !is_default? && set_as_default?
400 400 self.is_default = true
401 401 end
402 402 if is_default? && is_default_changed?
403 403 Repository.update_all(["is_default = ?", false], ["project_id = ?", project_id])
404 404 end
405 405 end
406 406
407 407 def load_entries_changesets(entries)
408 408 if entries
409 409 entries.each do |entry|
410 410 if entry.lastrev && entry.lastrev.identifier
411 411 entry.changeset = find_changeset_by_name(entry.lastrev.identifier)
412 412 end
413 413 end
414 414 end
415 415 end
416 416
417 417 private
418 418
419 419 # Deletes repository data
420 420 def clear_changesets
421 421 cs = Changeset.table_name
422 422 ch = Change.table_name
423 423 ci = "#{table_name_prefix}changesets_issues#{table_name_suffix}"
424 424 cp = "#{table_name_prefix}changeset_parents#{table_name_suffix}"
425 425
426 426 connection.delete("DELETE FROM #{ch} WHERE #{ch}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
427 427 connection.delete("DELETE FROM #{ci} WHERE #{ci}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
428 428 connection.delete("DELETE FROM #{cp} WHERE #{cp}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
429 429 connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}")
430 clear_extra_info_of_changesets
431 end
432
433 def clear_extra_info_of_changesets
430 434 end
431 435 end
@@ -1,258 +1,269
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2012 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 def self.human_attribute_name(attribute_key_name, *args)
26 26 attr_name = attribute_key_name.to_s
27 27 if attr_name == "url"
28 28 attr_name = "path_to_repository"
29 29 end
30 30 super(attr_name, *args)
31 31 end
32 32
33 33 def self.scm_adapter_class
34 34 Redmine::Scm::Adapters::GitAdapter
35 35 end
36 36
37 37 def self.scm_name
38 38 'Git'
39 39 end
40 40
41 41 def report_last_commit
42 42 extra_report_last_commit
43 43 end
44 44
45 45 def extra_report_last_commit
46 46 return false if extra_info.nil?
47 47 v = extra_info["extra_report_last_commit"]
48 48 return false if v.nil?
49 49 v.to_s != '0'
50 50 end
51 51
52 52 def supports_directory_revisions?
53 53 true
54 54 end
55 55
56 56 def supports_revision_graph?
57 57 true
58 58 end
59 59
60 60 def repo_log_encoding
61 61 'UTF-8'
62 62 end
63 63
64 64 # Returns the identifier for the given git changeset
65 65 def self.changeset_identifier(changeset)
66 66 changeset.scmid
67 67 end
68 68
69 69 # Returns the readable identifier for the given git changeset
70 70 def self.format_changeset_identifier(changeset)
71 71 changeset.revision[0, 8]
72 72 end
73 73
74 74 def branches
75 75 scm.branches
76 76 end
77 77
78 78 def tags
79 79 scm.tags
80 80 end
81 81
82 82 def default_branch
83 83 scm.default_branch
84 84 rescue Exception => e
85 85 logger.error "git: error during get default branch: #{e.message}"
86 86 nil
87 87 end
88 88
89 89 def find_changeset_by_name(name)
90 90 if name.present?
91 91 changesets.where(:revision => name.to_s).first ||
92 92 changesets.where('scmid LIKE ?', "#{name}%").first
93 93 end
94 94 end
95 95
96 96 def entries(path=nil, identifier=nil)
97 97 entries = scm.entries(path, identifier, :report_last_commit => extra_report_last_commit)
98 98 load_entries_changesets(entries)
99 99 entries
100 100 end
101 101
102 102 # With SCMs that have a sequential commit numbering,
103 103 # such as Subversion and Mercurial,
104 104 # Redmine is able to be clever and only fetch changesets
105 105 # going forward from the most recent one it knows about.
106 106 #
107 107 # However, Git does not have a sequential commit numbering.
108 108 #
109 109 # In order to fetch only new adding revisions,
110 110 # Redmine needs to save "heads".
111 111 #
112 112 # In Git and Mercurial, revisions are not in date order.
113 113 # Redmine Mercurial fixed issues.
114 114 # * Redmine Takes Too Long On Large Mercurial Repository
115 115 # http://www.redmine.org/issues/3449
116 116 # * Sorting for changesets might go wrong on Mercurial repos
117 117 # http://www.redmine.org/issues/3567
118 118 #
119 119 # Database revision column is text, so Redmine can not sort by revision.
120 120 # Mercurial has revision number, and revision number guarantees revision order.
121 121 # Redmine Mercurial model stored revisions ordered by database id to database.
122 122 # So, Redmine Mercurial model can use correct ordering revisions.
123 123 #
124 124 # Redmine Mercurial adapter uses "hg log -r 0:tip --limit 10"
125 125 # to get limited revisions from old to new.
126 126 # But, Git 1.7.3.4 does not support --reverse with -n or --skip.
127 127 #
128 128 # The repository can still be fully reloaded by calling #clear_changesets
129 129 # before fetching changesets (eg. for offline resync)
130 130 def fetch_changesets
131 131 scm_brs = branches
132 132 return if scm_brs.nil? || scm_brs.empty?
133 133
134 134 h1 = extra_info || {}
135 135 h = h1.dup
136 136 repo_heads = scm_brs.map{ |br| br.scmid }
137 137 h["heads"] ||= []
138 138 prev_db_heads = h["heads"].dup
139 139 if prev_db_heads.empty?
140 140 prev_db_heads += heads_from_branches_hash
141 141 end
142 142 return if prev_db_heads.sort == repo_heads.sort
143 143
144 144 h["db_consistent"] ||= {}
145 145 if changesets.count == 0
146 146 h["db_consistent"]["ordering"] = 1
147 147 merge_extra_info(h)
148 148 self.save
149 149 elsif ! h["db_consistent"].has_key?("ordering")
150 150 h["db_consistent"]["ordering"] = 0
151 151 merge_extra_info(h)
152 152 self.save
153 153 end
154 154 save_revisions(prev_db_heads, repo_heads)
155 155 end
156 156
157 157 def save_revisions(prev_db_heads, repo_heads)
158 158 h = {}
159 159 opts = {}
160 160 opts[:reverse] = true
161 161 opts[:excludes] = prev_db_heads
162 162 opts[:includes] = repo_heads
163 163
164 164 revisions = scm.revisions('', nil, nil, opts)
165 165 return if revisions.blank?
166 166
167 167 # Make the search for existing revisions in the database in a more sufficient manner
168 168 #
169 169 # Git branch is the reference to the specific revision.
170 170 # Git can *delete* remote branch and *re-push* branch.
171 171 #
172 172 # $ git push remote :branch
173 173 # $ git push remote branch
174 174 #
175 175 # After deleting branch, revisions remain in repository until "git gc".
176 176 # On git 1.7.2.3, default pruning date is 2 weeks.
177 177 # So, "git log --not deleted_branch_head_revision" return code is 0.
178 178 #
179 179 # After re-pushing branch, "git log" returns revisions which are saved in database.
180 180 # So, Redmine needs to scan revisions and database every time.
181 181 #
182 182 # This is replacing the one-after-one queries.
183 183 # Find all revisions, that are in the database, and then remove them from the revision array.
184 184 # Then later we won't need any conditions for db existence.
185 185 # Query for several revisions at once, and remove them from the revisions array, if they are there.
186 186 # Do this in chunks, to avoid eventual memory problems (in case of tens of thousands of commits).
187 187 # If there are no revisions (because the original code's algorithm filtered them),
188 188 # then this part will be stepped over.
189 189 # We make queries, just if there is any revision.
190 190 limit = 100
191 191 offset = 0
192 192 revisions_copy = revisions.clone # revisions will change
193 193 while offset < revisions_copy.size
194 194 recent_changesets_slice = changesets.find(
195 195 :all,
196 196 :conditions => [
197 197 'scmid IN (?)',
198 198 revisions_copy.slice(offset, limit).map{|x| x.scmid}
199 199 ]
200 200 )
201 201 # Subtract revisions that redmine already knows about
202 202 recent_revisions = recent_changesets_slice.map{|c| c.scmid}
203 203 revisions.reject!{|r| recent_revisions.include?(r.scmid)}
204 204 offset += limit
205 205 end
206 206
207 207 revisions.each do |rev|
208 208 transaction do
209 209 # There is no search in the db for this revision, because above we ensured,
210 210 # that it's not in the db.
211 211 save_revision(rev)
212 212 end
213 213 end
214 214 h["heads"] = repo_heads.dup
215 215 merge_extra_info(h)
216 216 self.save
217 217 end
218 218 private :save_revisions
219 219
220 220 def save_revision(rev)
221 221 parents = (rev.parents || []).collect{|rp| find_changeset_by_name(rp)}.compact
222 222 changeset = Changeset.create(
223 223 :repository => self,
224 224 :revision => rev.identifier,
225 225 :scmid => rev.scmid,
226 226 :committer => rev.author,
227 227 :committed_on => rev.time,
228 228 :comments => rev.message,
229 229 :parents => parents
230 230 )
231 231 unless changeset.new_record?
232 232 rev.paths.each { |change| changeset.create_change(change) }
233 233 end
234 234 changeset
235 235 end
236 236 private :save_revision
237 237
238 238 def heads_from_branches_hash
239 239 h1 = extra_info || {}
240 240 h = h1.dup
241 241 h["branches"] ||= {}
242 242 h['branches'].map{|br, hs| hs['last_scmid']}
243 243 end
244 244
245 245 def latest_changesets(path,rev,limit=10)
246 246 revisions = scm.revisions(path, nil, rev, :limit => limit, :all => false)
247 247 return [] if revisions.nil? || revisions.empty?
248 248
249 249 changesets.find(
250 250 :all,
251 251 :conditions => [
252 252 "scmid IN (?)",
253 253 revisions.map!{|c| c.scmid}
254 254 ],
255 255 :order => 'committed_on DESC'
256 256 )
257 257 end
258
259 def clear_extra_info_of_changesets
260 return if extra_info.nil?
261 v = extra_info["extra_report_last_commit"]
262 write_attribute(:extra_info, nil)
263 h = {}
264 h["extra_report_last_commit"] = v
265 merge_extra_info(h)
266 self.save
267 end
268 private :clear_extra_info_of_changesets
258 269 end
@@ -1,567 +1,601
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2012 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 File.expand_path('../../test_helper', __FILE__)
19 19
20 20 class RepositoryGitTest < ActiveSupport::TestCase
21 21 fixtures :projects, :repositories, :enabled_modules, :users, :roles
22 22
23 23 include Redmine::I18n
24 24
25 25 REPOSITORY_PATH = Rails.root.join('tmp/test/git_repository').to_s
26 26 REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin?
27 27
28 28 NUM_REV = 28
29 29 NUM_HEAD = 6
30 30
31 31 FELIX_HEX = "Felix Sch\xC3\xA4fer"
32 32 CHAR_1_HEX = "\xc3\x9c"
33 33
34 34 ## Git, Mercurial and CVS path encodings are binary.
35 35 ## Subversion supports URL encoding for path.
36 36 ## Redmine Mercurial adapter and extension use URL encoding.
37 37 ## Git accepts only binary path in command line parameter.
38 38 ## So, there is no way to use binary command line parameter in JRuby.
39 39 JRUBY_SKIP = (RUBY_PLATFORM == 'java')
40 40 JRUBY_SKIP_STR = "TODO: This test fails in JRuby"
41 41
42 42 def setup
43 43 @project = Project.find(3)
44 44 @repository = Repository::Git.create(
45 45 :project => @project,
46 46 :url => REPOSITORY_PATH,
47 47 :path_encoding => 'ISO-8859-1'
48 48 )
49 49 assert @repository
50 50 @char_1 = CHAR_1_HEX.dup
51 51 if @char_1.respond_to?(:force_encoding)
52 52 @char_1.force_encoding('UTF-8')
53 53 end
54 54 end
55 55
56 56 def test_blank_path_to_repository_error_message
57 57 set_language_if_valid 'en'
58 58 repo = Repository::Git.new(
59 59 :project => @project,
60 60 :identifier => 'test'
61 61 )
62 62 assert !repo.save
63 63 assert_include "Path to repository can't be blank",
64 64 repo.errors.full_messages
65 65 end
66 66
67 67 def test_blank_path_to_repository_error_message_fr
68 68 set_language_if_valid 'fr'
69 69 str = "Chemin du d\xc3\xa9p\xc3\xb4t doit \xc3\xaatre renseign\xc3\xa9(e)"
70 70 str.force_encoding('UTF-8') if str.respond_to?(:force_encoding)
71 71 repo = Repository::Git.new(
72 72 :project => @project,
73 73 :url => "",
74 74 :identifier => 'test',
75 75 :path_encoding => ''
76 76 )
77 77 assert !repo.save
78 78 assert_include str, repo.errors.full_messages
79 79 end
80 80
81 81 if File.directory?(REPOSITORY_PATH)
82 82 ## Ruby uses ANSI api to fork a process on Windows.
83 83 ## Japanese Shift_JIS and Traditional Chinese Big5 have 0x5c(backslash) problem
84 84 ## and these are incompatible with ASCII.
85 85 ## Git for Windows (msysGit) changed internal API from ANSI to Unicode in 1.7.10
86 86 ## http://code.google.com/p/msysgit/issues/detail?id=80
87 87 ## So, Latin-1 path tests fail on Japanese Windows
88 88 WINDOWS_PASS = (Redmine::Platform.mswin? &&
89 89 Redmine::Scm::Adapters::GitAdapter.client_version_above?([1, 7, 10]))
90 90 WINDOWS_SKIP_STR = "TODO: This test fails in Git for Windows above 1.7.10"
91 91
92 92 def test_scm_available
93 93 klass = Repository::Git
94 94 assert_equal "Git", klass.scm_name
95 95 assert klass.scm_adapter_class
96 96 assert_not_equal "", klass.scm_command
97 97 assert_equal true, klass.scm_available
98 98 end
99 99
100 100 def test_entries
101 101 entries = @repository.entries
102 102 assert_kind_of Redmine::Scm::Adapters::Entries, entries
103 103 end
104 104
105 105 def test_fetch_changesets_from_scratch
106 106 assert_nil @repository.extra_info
107 107
108 108 assert_equal 0, @repository.changesets.count
109 109 @repository.fetch_changesets
110 110 @project.reload
111 111
112 112 assert_equal NUM_REV, @repository.changesets.count
113 113 assert_equal 39, @repository.filechanges.count
114 114
115 115 commit = @repository.changesets.find_by_revision("7234cb2750b63f47bff735edc50a1c0a433c2518")
116 116 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518", commit.scmid
117 117 assert_equal "Initial import.\nThe repository contains 3 files.", commit.comments
118 118 assert_equal "jsmith <jsmith@foo.bar>", commit.committer
119 119 assert_equal User.find_by_login('jsmith'), commit.user
120 120 # TODO: add a commit with commit time <> author time to the test repository
121 121 assert_equal "2007-12-14 09:22:52".to_time, commit.committed_on
122 122 assert_equal "2007-12-14".to_date, commit.commit_date
123 123 assert_equal 3, commit.filechanges.count
124 124 change = commit.filechanges.sort_by(&:path).first
125 125 assert_equal "README", change.path
126 126 assert_equal nil, change.from_path
127 127 assert_equal "A", change.action
128 128
129 129 assert_equal NUM_HEAD, @repository.extra_info["heads"].size
130 130 end
131 131
132 132 def test_fetch_changesets_incremental
133 133 assert_equal 0, @repository.changesets.count
134 134 @repository.fetch_changesets
135 135 @project.reload
136 136 assert_equal NUM_REV, @repository.changesets.count
137 137 extra_info_heads = @repository.extra_info["heads"].dup
138 138 assert_equal NUM_HEAD, extra_info_heads.size
139 139 extra_info_heads.delete_if { |x| x == "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c" }
140 140 assert_equal 4, extra_info_heads.size
141 141
142 142 del_revs = [
143 143 "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c",
144 144 "ed5bb786bbda2dee66a2d50faf51429dbc043a7b",
145 145 "4f26664364207fa8b1af9f8722647ab2d4ac5d43",
146 146 "deff712f05a90d96edbd70facc47d944be5897e3",
147 147 "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf",
148 148 "7e61ac704deecde634b51e59daa8110435dcb3da",
149 149 ]
150 150 @repository.changesets.each do |rev|
151 151 rev.destroy if del_revs.detect {|r| r == rev.scmid.to_s }
152 152 end
153 153 @project.reload
154 154 cs1 = @repository.changesets
155 155 assert_equal NUM_REV - 6, cs1.count
156 156 extra_info_heads << "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8"
157 157 h = {}
158 158 h["heads"] = extra_info_heads
159 159 @repository.merge_extra_info(h)
160 160 @repository.save
161 161 @project.reload
162 162 assert @repository.extra_info["heads"].index("4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8")
163 163 @repository.fetch_changesets
164 164 @project.reload
165 165 assert_equal NUM_REV, @repository.changesets.count
166 166 assert_equal NUM_HEAD, @repository.extra_info["heads"].size
167 167 assert @repository.extra_info["heads"].index("83ca5fd546063a3c7dc2e568ba3355661a9e2b2c")
168 168 end
169 169
170 170 def test_fetch_changesets_history_editing
171 171 assert_equal 0, @repository.changesets.count
172 172 @repository.fetch_changesets
173 173 @project.reload
174 174 assert_equal NUM_REV, @repository.changesets.count
175 175 extra_info_heads = @repository.extra_info["heads"].dup
176 176 assert_equal NUM_HEAD, extra_info_heads.size
177 177 extra_info_heads.delete_if { |x| x == "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c" }
178 178 assert_equal 4, extra_info_heads.size
179 179
180 180 del_revs = [
181 181 "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c",
182 182 "ed5bb786bbda2dee66a2d50faf51429dbc043a7b",
183 183 "4f26664364207fa8b1af9f8722647ab2d4ac5d43",
184 184 "deff712f05a90d96edbd70facc47d944be5897e3",
185 185 "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf",
186 186 "7e61ac704deecde634b51e59daa8110435dcb3da",
187 187 ]
188 188 @repository.changesets.each do |rev|
189 189 rev.destroy if del_revs.detect {|r| r == rev.scmid.to_s }
190 190 end
191 191 @project.reload
192 192 assert_equal NUM_REV - 6, @repository.changesets.count
193 193
194 194 c = Changeset.new(:repository => @repository,
195 195 :committed_on => Time.now,
196 196 :revision => "abcd1234efgh",
197 197 :scmid => "abcd1234efgh",
198 198 :comments => 'test')
199 199 assert c.save
200 200 @project.reload
201 201 assert_equal NUM_REV - 5, @repository.changesets.count
202 202
203 203 extra_info_heads << "1234abcd5678"
204 204 h = {}
205 205 h["heads"] = extra_info_heads
206 206 @repository.merge_extra_info(h)
207 207 @repository.save
208 208 @project.reload
209 209 h1 = @repository.extra_info["heads"].dup
210 210 assert h1.index("1234abcd5678")
211 211 assert_equal 5, h1.size
212 212
213 213 @repository.fetch_changesets
214 214 @project.reload
215 215 assert_equal NUM_REV - 5, @repository.changesets.count
216 216 h2 = @repository.extra_info["heads"].dup
217 217 assert_equal h1, h2
218 218 end
219 219
220 def test_keep_extra_report_last_commit_in_clear_changesets
221 assert_nil @repository.extra_info
222 h = {}
223 h["extra_report_last_commit"] = 1
224 @repository.merge_extra_info(h)
225 @repository.save
226 @project.reload
227
228 assert_equal 0, @repository.changesets.count
229 @repository.fetch_changesets
230 @project.reload
231
232 assert_equal NUM_REV, @repository.changesets.count
233 @repository.send(:clear_changesets)
234 assert_equal 1, @repository.extra_info.size
235 assert_equal 1, @repository.extra_info["extra_report_last_commit"]
236 end
237
238 def test_refetch_after_clear_changesets
239 assert_nil @repository.extra_info
240 assert_equal 0, @repository.changesets.count
241 @repository.fetch_changesets
242 @project.reload
243 assert_equal NUM_REV, @repository.changesets.count
244
245 @repository.send(:clear_changesets)
246 @project.reload
247 assert_equal 0, @repository.changesets.count
248
249 @repository.fetch_changesets
250 @project.reload
251 assert_equal NUM_REV, @repository.changesets.count
252 end
253
220 254 def test_parents
221 255 assert_equal 0, @repository.changesets.count
222 256 @repository.fetch_changesets
223 257 @project.reload
224 258 assert_equal NUM_REV, @repository.changesets.count
225 259 r1 = @repository.find_changeset_by_name("7234cb2750b63")
226 260 assert_equal [], r1.parents
227 261 r2 = @repository.find_changeset_by_name("899a15dba03a3")
228 262 assert_equal 1, r2.parents.length
229 263 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
230 264 r2.parents[0].identifier
231 265 r3 = @repository.find_changeset_by_name("32ae898b720c2")
232 266 assert_equal 2, r3.parents.length
233 267 r4 = [r3.parents[0].identifier, r3.parents[1].identifier].sort
234 268 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", r4[0]
235 269 assert_equal "7e61ac704deecde634b51e59daa8110435dcb3da", r4[1]
236 270 end
237 271
238 272 def test_db_consistent_ordering_init
239 273 assert_nil @repository.extra_info
240 274 assert_equal 0, @repository.changesets.count
241 275 @repository.fetch_changesets
242 276 @project.reload
243 277 assert_equal 1, @repository.extra_info["db_consistent"]["ordering"]
244 278 end
245 279
246 280 def test_db_consistent_ordering_before_1_2
247 281 assert_nil @repository.extra_info
248 282 assert_equal 0, @repository.changesets.count
249 283 @repository.fetch_changesets
250 284 @project.reload
251 285 assert_equal NUM_REV, @repository.changesets.count
252 286 assert_not_nil @repository.extra_info
253 287 h = {}
254 288 h["heads"] = []
255 289 h["branches"] = {}
256 290 h["db_consistent"] = {}
257 291 @repository.merge_extra_info(h)
258 292 @repository.save
259 293 assert_equal NUM_REV, @repository.changesets.count
260 294 @repository.fetch_changesets
261 295 @project.reload
262 296 assert_equal 0, @repository.extra_info["db_consistent"]["ordering"]
263 297
264 298 extra_info_heads = @repository.extra_info["heads"].dup
265 299 extra_info_heads.delete_if { |x| x == "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c" }
266 300 del_revs = [
267 301 "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c",
268 302 "ed5bb786bbda2dee66a2d50faf51429dbc043a7b",
269 303 "4f26664364207fa8b1af9f8722647ab2d4ac5d43",
270 304 "deff712f05a90d96edbd70facc47d944be5897e3",
271 305 "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf",
272 306 "7e61ac704deecde634b51e59daa8110435dcb3da",
273 307 ]
274 308 @repository.changesets.each do |rev|
275 309 rev.destroy if del_revs.detect {|r| r == rev.scmid.to_s }
276 310 end
277 311 @project.reload
278 312 cs1 = @repository.changesets
279 313 assert_equal NUM_REV - 6, cs1.count
280 314 assert_equal 0, @repository.extra_info["db_consistent"]["ordering"]
281 315
282 316 extra_info_heads << "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8"
283 317 h = {}
284 318 h["heads"] = extra_info_heads
285 319 @repository.merge_extra_info(h)
286 320 @repository.save
287 321 @project.reload
288 322 assert @repository.extra_info["heads"].index("4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8")
289 323 @repository.fetch_changesets
290 324 @project.reload
291 325 assert_equal NUM_REV, @repository.changesets.count
292 326 assert_equal NUM_HEAD, @repository.extra_info["heads"].size
293 327
294 328 assert_equal 0, @repository.extra_info["db_consistent"]["ordering"]
295 329 end
296 330
297 331 def test_heads_from_branches_hash
298 332 assert_nil @repository.extra_info
299 333 assert_equal 0, @repository.changesets.count
300 334 assert_equal [], @repository.heads_from_branches_hash
301 335 h = {}
302 336 h["branches"] = {}
303 337 h["branches"]["test1"] = {}
304 338 h["branches"]["test1"]["last_scmid"] = "1234abcd"
305 339 h["branches"]["test2"] = {}
306 340 h["branches"]["test2"]["last_scmid"] = "abcd1234"
307 341 @repository.merge_extra_info(h)
308 342 @repository.save
309 343 @project.reload
310 344 assert_equal ["1234abcd", "abcd1234"], @repository.heads_from_branches_hash.sort
311 345 end
312 346
313 347 def test_latest_changesets
314 348 assert_equal 0, @repository.changesets.count
315 349 @repository.fetch_changesets
316 350 @project.reload
317 351 assert_equal NUM_REV, @repository.changesets.count
318 352 # with limit
319 353 changesets = @repository.latest_changesets('', 'master', 2)
320 354 assert_equal 2, changesets.size
321 355
322 356 # with path
323 357 changesets = @repository.latest_changesets('images', 'master')
324 358 assert_equal [
325 359 'deff712f05a90d96edbd70facc47d944be5897e3',
326 360 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
327 361 '7234cb2750b63f47bff735edc50a1c0a433c2518',
328 362 ], changesets.collect(&:revision)
329 363
330 364 changesets = @repository.latest_changesets('README', nil)
331 365 assert_equal [
332 366 '32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf',
333 367 '4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8',
334 368 '713f4944648826f558cf548222f813dabe7cbb04',
335 369 '61b685fbe55ab05b5ac68402d5720c1a6ac973d1',
336 370 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
337 371 '7234cb2750b63f47bff735edc50a1c0a433c2518',
338 372 ], changesets.collect(&:revision)
339 373
340 374 # with path, revision and limit
341 375 changesets = @repository.latest_changesets('images', '899a15dba')
342 376 assert_equal [
343 377 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
344 378 '7234cb2750b63f47bff735edc50a1c0a433c2518',
345 379 ], changesets.collect(&:revision)
346 380
347 381 changesets = @repository.latest_changesets('images', '899a15dba', 1)
348 382 assert_equal [
349 383 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
350 384 ], changesets.collect(&:revision)
351 385
352 386 changesets = @repository.latest_changesets('README', '899a15dba')
353 387 assert_equal [
354 388 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
355 389 '7234cb2750b63f47bff735edc50a1c0a433c2518',
356 390 ], changesets.collect(&:revision)
357 391
358 392 changesets = @repository.latest_changesets('README', '899a15dba', 1)
359 393 assert_equal [
360 394 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
361 395 ], changesets.collect(&:revision)
362 396
363 397 # with path, tag and limit
364 398 changesets = @repository.latest_changesets('images', 'tag01.annotated')
365 399 assert_equal [
366 400 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
367 401 '7234cb2750b63f47bff735edc50a1c0a433c2518',
368 402 ], changesets.collect(&:revision)
369 403
370 404 changesets = @repository.latest_changesets('images', 'tag01.annotated', 1)
371 405 assert_equal [
372 406 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
373 407 ], changesets.collect(&:revision)
374 408
375 409 changesets = @repository.latest_changesets('README', 'tag01.annotated')
376 410 assert_equal [
377 411 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
378 412 '7234cb2750b63f47bff735edc50a1c0a433c2518',
379 413 ], changesets.collect(&:revision)
380 414
381 415 changesets = @repository.latest_changesets('README', 'tag01.annotated', 1)
382 416 assert_equal [
383 417 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
384 418 ], changesets.collect(&:revision)
385 419
386 420 # with path, branch and limit
387 421 changesets = @repository.latest_changesets('images', 'test_branch')
388 422 assert_equal [
389 423 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
390 424 '7234cb2750b63f47bff735edc50a1c0a433c2518',
391 425 ], changesets.collect(&:revision)
392 426
393 427 changesets = @repository.latest_changesets('images', 'test_branch', 1)
394 428 assert_equal [
395 429 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
396 430 ], changesets.collect(&:revision)
397 431
398 432 changesets = @repository.latest_changesets('README', 'test_branch')
399 433 assert_equal [
400 434 '713f4944648826f558cf548222f813dabe7cbb04',
401 435 '61b685fbe55ab05b5ac68402d5720c1a6ac973d1',
402 436 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
403 437 '7234cb2750b63f47bff735edc50a1c0a433c2518',
404 438 ], changesets.collect(&:revision)
405 439
406 440 changesets = @repository.latest_changesets('README', 'test_branch', 2)
407 441 assert_equal [
408 442 '713f4944648826f558cf548222f813dabe7cbb04',
409 443 '61b685fbe55ab05b5ac68402d5720c1a6ac973d1',
410 444 ], changesets.collect(&:revision)
411 445
412 446 if WINDOWS_PASS
413 447 puts WINDOWS_SKIP_STR
414 448 elsif JRUBY_SKIP
415 449 puts JRUBY_SKIP_STR
416 450 else
417 451 # latin-1 encoding path
418 452 changesets = @repository.latest_changesets(
419 453 "latin-1-dir/test-#{@char_1}-2.txt", '64f1f3e89')
420 454 assert_equal [
421 455 '64f1f3e89ad1cb57976ff0ad99a107012ba3481d',
422 456 '4fc55c43bf3d3dc2efb66145365ddc17639ce81e',
423 457 ], changesets.collect(&:revision)
424 458
425 459 changesets = @repository.latest_changesets(
426 460 "latin-1-dir/test-#{@char_1}-2.txt", '64f1f3e89', 1)
427 461 assert_equal [
428 462 '64f1f3e89ad1cb57976ff0ad99a107012ba3481d',
429 463 ], changesets.collect(&:revision)
430 464 end
431 465 end
432 466
433 467 def test_latest_changesets_latin_1_dir
434 468 if WINDOWS_PASS
435 469 puts WINDOWS_SKIP_STR
436 470 elsif JRUBY_SKIP
437 471 puts JRUBY_SKIP_STR
438 472 else
439 473 assert_equal 0, @repository.changesets.count
440 474 @repository.fetch_changesets
441 475 @project.reload
442 476 assert_equal NUM_REV, @repository.changesets.count
443 477 changesets = @repository.latest_changesets(
444 478 "latin-1-dir/test-#{@char_1}-subdir", '1ca7f5ed')
445 479 assert_equal [
446 480 '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127',
447 481 ], changesets.collect(&:revision)
448 482 end
449 483 end
450 484
451 485 def test_find_changeset_by_name
452 486 assert_equal 0, @repository.changesets.count
453 487 @repository.fetch_changesets
454 488 @project.reload
455 489 assert_equal NUM_REV, @repository.changesets.count
456 490 ['7234cb2750b63f47bff735edc50a1c0a433c2518', '7234cb2750b'].each do |r|
457 491 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518',
458 492 @repository.find_changeset_by_name(r).revision
459 493 end
460 494 end
461 495
462 496 def test_find_changeset_by_empty_name
463 497 assert_equal 0, @repository.changesets.count
464 498 @repository.fetch_changesets
465 499 @project.reload
466 500 assert_equal NUM_REV, @repository.changesets.count
467 501 ['', ' ', nil].each do |r|
468 502 assert_nil @repository.find_changeset_by_name(r)
469 503 end
470 504 end
471 505
472 506 def test_identifier
473 507 assert_equal 0, @repository.changesets.count
474 508 @repository.fetch_changesets
475 509 @project.reload
476 510 assert_equal NUM_REV, @repository.changesets.count
477 511 c = @repository.changesets.find_by_revision(
478 512 '7234cb2750b63f47bff735edc50a1c0a433c2518')
479 513 assert_equal c.scmid, c.identifier
480 514 end
481 515
482 516 def test_format_identifier
483 517 assert_equal 0, @repository.changesets.count
484 518 @repository.fetch_changesets
485 519 @project.reload
486 520 assert_equal NUM_REV, @repository.changesets.count
487 521 c = @repository.changesets.find_by_revision(
488 522 '7234cb2750b63f47bff735edc50a1c0a433c2518')
489 523 assert_equal '7234cb27', c.format_identifier
490 524 end
491 525
492 526 def test_activities
493 527 c = Changeset.new(:repository => @repository,
494 528 :committed_on => Time.now,
495 529 :revision => 'abc7234cb2750b63f47bff735edc50a1c0a433c2',
496 530 :scmid => 'abc7234cb2750b63f47bff735edc50a1c0a433c2',
497 531 :comments => 'test')
498 532 assert c.event_title.include?('abc7234c:')
499 533 assert_equal 'abc7234cb2750b63f47bff735edc50a1c0a433c2', c.event_url[:rev]
500 534 end
501 535
502 536 def test_log_utf8
503 537 assert_equal 0, @repository.changesets.count
504 538 @repository.fetch_changesets
505 539 @project.reload
506 540 assert_equal NUM_REV, @repository.changesets.count
507 541 str_felix_hex = FELIX_HEX.dup
508 542 if str_felix_hex.respond_to?(:force_encoding)
509 543 str_felix_hex.force_encoding('UTF-8')
510 544 end
511 545 c = @repository.changesets.find_by_revision(
512 546 'ed5bb786bbda2dee66a2d50faf51429dbc043a7b')
513 547 assert_equal "#{str_felix_hex} <felix@fachschaften.org>", c.committer
514 548 end
515 549
516 550 def test_previous
517 551 assert_equal 0, @repository.changesets.count
518 552 @repository.fetch_changesets
519 553 @project.reload
520 554 assert_equal NUM_REV, @repository.changesets.count
521 555 %w|1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127 1ca7f5ed|.each do |r1|
522 556 changeset = @repository.find_changeset_by_name(r1)
523 557 %w|64f1f3e89ad1cb57976ff0ad99a107012ba3481d 64f1f3e89ad1|.each do |r2|
524 558 assert_equal @repository.find_changeset_by_name(r2), changeset.previous
525 559 end
526 560 end
527 561 end
528 562
529 563 def test_previous_nil
530 564 assert_equal 0, @repository.changesets.count
531 565 @repository.fetch_changesets
532 566 @project.reload
533 567 assert_equal NUM_REV, @repository.changesets.count
534 568 %w|7234cb2750b63f47bff735edc50a1c0a433c2518 7234cb275|.each do |r1|
535 569 changeset = @repository.find_changeset_by_name(r1)
536 570 assert_nil changeset.previous
537 571 end
538 572 end
539 573
540 574 def test_next
541 575 assert_equal 0, @repository.changesets.count
542 576 @repository.fetch_changesets
543 577 @project.reload
544 578 assert_equal NUM_REV, @repository.changesets.count
545 579 %w|64f1f3e89ad1cb57976ff0ad99a107012ba3481d 64f1f3e89ad1|.each do |r2|
546 580 changeset = @repository.find_changeset_by_name(r2)
547 581 %w|1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127 1ca7f5ed|.each do |r1|
548 582 assert_equal @repository.find_changeset_by_name(r1), changeset.next
549 583 end
550 584 end
551 585 end
552 586
553 587 def test_next_nil
554 588 assert_equal 0, @repository.changesets.count
555 589 @repository.fetch_changesets
556 590 @project.reload
557 591 assert_equal NUM_REV, @repository.changesets.count
558 592 %w|2a682156a3b6e77a8bf9cd4590e8db757f3c6c78 2a682156a3b6e77a|.each do |r1|
559 593 changeset = @repository.find_changeset_by_name(r1)
560 594 assert_nil changeset.next
561 595 end
562 596 end
563 597 else
564 598 puts "Git test repository NOT FOUND. Skipping unit tests !!!"
565 599 def test_fake; assert true end
566 600 end
567 601 end
General Comments 0
You need to be logged in to leave comments. Login now