##// END OF EJS Templates
Merged r14050 (#19260)....
Jean-Philippe Lang -
r13676:36b70437e0fc
parent child
Show More
@@ -1,510 +1,509
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2015 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, lambda{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 validates_presence_of :identifier, :unless => Proc.new { |r| r.is_default? || r.set_as_default? }
42 41 validates_uniqueness_of :identifier, :scope => :project_id, :allow_blank => true
43 42 validates_exclusion_of :identifier, :in => %w(browse show entry raw changes annotate diff statistics graph revisions revision)
44 43 # donwcase letters, digits, dashes, underscores but not digits only
45 44 validates_format_of :identifier, :with => /\A(?!\d+$)[a-z0-9\-_]*\z/, :allow_blank => true
46 45 # Checks if the SCM is enabled when creating a repository
47 46 validate :repo_create_validation, :on => :create
48 47 validate :validate_repository_path
49 48 attr_protected :id
50 49
51 50 safe_attributes 'identifier',
52 51 'login',
53 52 'password',
54 53 'path_encoding',
55 54 'log_encoding',
56 55 'is_default'
57 56
58 57 safe_attributes 'url',
59 58 :if => lambda {|repository, user| repository.new_record?}
60 59
61 60 def repo_create_validation
62 61 unless Setting.enabled_scm.include?(self.class.name.demodulize)
63 62 errors.add(:type, :invalid)
64 63 end
65 64 end
66 65
67 66 def self.human_attribute_name(attribute_key_name, *args)
68 67 attr_name = attribute_key_name.to_s
69 68 if attr_name == "log_encoding"
70 69 attr_name = "commit_logs_encoding"
71 70 end
72 71 super(attr_name, *args)
73 72 end
74 73
75 74 # Removes leading and trailing whitespace
76 75 def url=(arg)
77 76 write_attribute(:url, arg ? arg.to_s.strip : nil)
78 77 end
79 78
80 79 # Removes leading and trailing whitespace
81 80 def root_url=(arg)
82 81 write_attribute(:root_url, arg ? arg.to_s.strip : nil)
83 82 end
84 83
85 84 def password
86 85 read_ciphered_attribute(:password)
87 86 end
88 87
89 88 def password=(arg)
90 89 write_ciphered_attribute(:password, arg)
91 90 end
92 91
93 92 def scm_adapter
94 93 self.class.scm_adapter_class
95 94 end
96 95
97 96 def scm
98 97 unless @scm
99 98 @scm = self.scm_adapter.new(url, root_url,
100 99 login, password, path_encoding)
101 100 if root_url.blank? && @scm.root_url.present?
102 101 update_attribute(:root_url, @scm.root_url)
103 102 end
104 103 end
105 104 @scm
106 105 end
107 106
108 107 def scm_name
109 108 self.class.scm_name
110 109 end
111 110
112 111 def name
113 112 if identifier.present?
114 113 identifier
115 114 elsif is_default?
116 115 l(:field_repository_is_default)
117 116 else
118 117 scm_name
119 118 end
120 119 end
121 120
122 121 def identifier=(identifier)
123 122 super unless identifier_frozen?
124 123 end
125 124
126 125 def identifier_frozen?
127 126 errors[:identifier].blank? && !(new_record? || identifier.blank?)
128 127 end
129 128
130 129 def identifier_param
131 130 if is_default?
132 131 nil
133 132 elsif identifier.present?
134 133 identifier
135 134 else
136 135 id.to_s
137 136 end
138 137 end
139 138
140 139 def <=>(repository)
141 140 if is_default?
142 141 -1
143 142 elsif repository.is_default?
144 143 1
145 144 else
146 145 identifier.to_s <=> repository.identifier.to_s
147 146 end
148 147 end
149 148
150 149 def self.find_by_identifier_param(param)
151 150 if param.to_s =~ /^\d+$/
152 151 find_by_id(param)
153 152 else
154 153 find_by_identifier(param)
155 154 end
156 155 end
157 156
158 157 # TODO: should return an empty hash instead of nil to avoid many ||{}
159 158 def extra_info
160 159 h = read_attribute(:extra_info)
161 160 h.is_a?(Hash) ? h : nil
162 161 end
163 162
164 163 def merge_extra_info(arg)
165 164 h = extra_info || {}
166 165 return h if arg.nil?
167 166 h.merge!(arg)
168 167 write_attribute(:extra_info, h)
169 168 end
170 169
171 170 def report_last_commit
172 171 true
173 172 end
174 173
175 174 def supports_cat?
176 175 scm.supports_cat?
177 176 end
178 177
179 178 def supports_annotate?
180 179 scm.supports_annotate?
181 180 end
182 181
183 182 def supports_all_revisions?
184 183 true
185 184 end
186 185
187 186 def supports_directory_revisions?
188 187 false
189 188 end
190 189
191 190 def supports_revision_graph?
192 191 false
193 192 end
194 193
195 194 def entry(path=nil, identifier=nil)
196 195 scm.entry(path, identifier)
197 196 end
198 197
199 198 def scm_entries(path=nil, identifier=nil)
200 199 scm.entries(path, identifier)
201 200 end
202 201 protected :scm_entries
203 202
204 203 def entries(path=nil, identifier=nil)
205 204 entries = scm_entries(path, identifier)
206 205 load_entries_changesets(entries)
207 206 entries
208 207 end
209 208
210 209 def branches
211 210 scm.branches
212 211 end
213 212
214 213 def tags
215 214 scm.tags
216 215 end
217 216
218 217 def default_branch
219 218 nil
220 219 end
221 220
222 221 def properties(path, identifier=nil)
223 222 scm.properties(path, identifier)
224 223 end
225 224
226 225 def cat(path, identifier=nil)
227 226 scm.cat(path, identifier)
228 227 end
229 228
230 229 def diff(path, rev, rev_to)
231 230 scm.diff(path, rev, rev_to)
232 231 end
233 232
234 233 def diff_format_revisions(cs, cs_to, sep=':')
235 234 text = ""
236 235 text << cs_to.format_identifier + sep if cs_to
237 236 text << cs.format_identifier if cs
238 237 text
239 238 end
240 239
241 240 # Returns a path relative to the url of the repository
242 241 def relative_path(path)
243 242 path
244 243 end
245 244
246 245 # Finds and returns a revision with a number or the beginning of a hash
247 246 def find_changeset_by_name(name)
248 247 return nil if name.blank?
249 248 s = name.to_s
250 249 if s.match(/^\d*$/)
251 250 changesets.where("revision = ?", s).first
252 251 else
253 252 changesets.where("revision LIKE ?", s + '%').first
254 253 end
255 254 end
256 255
257 256 def latest_changeset
258 257 @latest_changeset ||= changesets.first
259 258 end
260 259
261 260 # Returns the latest changesets for +path+
262 261 # Default behaviour is to search in cached changesets
263 262 def latest_changesets(path, rev, limit=10)
264 263 if path.blank?
265 264 changesets.
266 265 reorder("#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC").
267 266 limit(limit).
268 267 preload(:user).
269 268 to_a
270 269 else
271 270 filechanges.
272 271 where("path = ?", path.with_leading_slash).
273 272 reorder("#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC").
274 273 limit(limit).
275 274 preload(:changeset => :user).
276 275 collect(&:changeset)
277 276 end
278 277 end
279 278
280 279 def scan_changesets_for_issue_ids
281 280 self.changesets.each(&:scan_comment_for_issue_ids)
282 281 end
283 282
284 283 # Returns an array of committers usernames and associated user_id
285 284 def committers
286 285 @committers ||= Changeset.where(:repository_id => id).uniq.pluck(:committer, :user_id)
287 286 end
288 287
289 288 # Maps committers username to a user ids
290 289 def committer_ids=(h)
291 290 if h.is_a?(Hash)
292 291 committers.each do |committer, user_id|
293 292 new_user_id = h[committer]
294 293 if new_user_id && (new_user_id.to_i != user_id.to_i)
295 294 new_user_id = (new_user_id.to_i > 0 ? new_user_id.to_i : nil)
296 295 Changeset.where(["repository_id = ? AND committer = ?", id, committer]).
297 296 update_all("user_id = #{new_user_id.nil? ? 'NULL' : new_user_id}")
298 297 end
299 298 end
300 299 @committers = nil
301 300 @found_committer_users = nil
302 301 true
303 302 else
304 303 false
305 304 end
306 305 end
307 306
308 307 # Returns the Redmine User corresponding to the given +committer+
309 308 # It will return nil if the committer is not yet mapped and if no User
310 309 # with the same username or email was found
311 310 def find_committer_user(committer)
312 311 unless committer.blank?
313 312 @found_committer_users ||= {}
314 313 return @found_committer_users[committer] if @found_committer_users.has_key?(committer)
315 314
316 315 user = nil
317 316 c = changesets.where(:committer => committer).
318 317 includes(:user).references(:user).first
319 318 if c && c.user
320 319 user = c.user
321 320 elsif committer.strip =~ /^([^<]+)(<(.*)>)?$/
322 321 username, email = $1.strip, $3
323 322 u = User.find_by_login(username)
324 323 u ||= User.find_by_mail(email) unless email.blank?
325 324 user = u
326 325 end
327 326 @found_committer_users[committer] = user
328 327 user
329 328 end
330 329 end
331 330
332 331 def repo_log_encoding
333 332 encoding = log_encoding.to_s.strip
334 333 encoding.blank? ? 'UTF-8' : encoding
335 334 end
336 335
337 336 # Fetches new changesets for all repositories of active projects
338 337 # Can be called periodically by an external script
339 338 # eg. ruby script/runner "Repository.fetch_changesets"
340 339 def self.fetch_changesets
341 340 Project.active.has_module(:repository).all.each do |project|
342 341 project.repositories.each do |repository|
343 342 begin
344 343 repository.fetch_changesets
345 344 rescue Redmine::Scm::Adapters::CommandFailed => e
346 345 logger.error "scm: error during fetching changesets: #{e.message}"
347 346 end
348 347 end
349 348 end
350 349 end
351 350
352 351 # scan changeset comments to find related and fixed issues for all repositories
353 352 def self.scan_changesets_for_issue_ids
354 353 all.each(&:scan_changesets_for_issue_ids)
355 354 end
356 355
357 356 def self.scm_name
358 357 'Abstract'
359 358 end
360 359
361 360 def self.available_scm
362 361 subclasses.collect {|klass| [klass.scm_name, klass.name]}
363 362 end
364 363
365 364 def self.factory(klass_name, *args)
366 365 klass = "Repository::#{klass_name}".constantize
367 366 klass.new(*args)
368 367 rescue
369 368 nil
370 369 end
371 370
372 371 def self.scm_adapter_class
373 372 nil
374 373 end
375 374
376 375 def self.scm_command
377 376 ret = ""
378 377 begin
379 378 ret = self.scm_adapter_class.client_command if self.scm_adapter_class
380 379 rescue Exception => e
381 380 logger.error "scm: error during get command: #{e.message}"
382 381 end
383 382 ret
384 383 end
385 384
386 385 def self.scm_version_string
387 386 ret = ""
388 387 begin
389 388 ret = self.scm_adapter_class.client_version_string if self.scm_adapter_class
390 389 rescue Exception => e
391 390 logger.error "scm: error during get version string: #{e.message}"
392 391 end
393 392 ret
394 393 end
395 394
396 395 def self.scm_available
397 396 ret = false
398 397 begin
399 398 ret = self.scm_adapter_class.client_available if self.scm_adapter_class
400 399 rescue Exception => e
401 400 logger.error "scm: error during get scm available: #{e.message}"
402 401 end
403 402 ret
404 403 end
405 404
406 405 def set_as_default?
407 406 new_record? && project && Repository.where(:project_id => project.id).empty?
408 407 end
409 408
410 409 # Returns a hash with statistics by author in the following form:
411 410 # {
412 411 # "John Smith" => { :commits => 45, :changes => 324 },
413 412 # "Bob" => { ... }
414 413 # }
415 414 #
416 415 # Notes:
417 416 # - this hash honnors the users mapping defined for the repository
418 417 def stats_by_author
419 418 commits = Changeset.where("repository_id = ?", id).select("committer, user_id, count(*) as count").group("committer, user_id")
420 419
421 420 #TODO: restore ordering ; this line probably never worked
422 421 #commits.to_a.sort! {|x, y| x.last <=> y.last}
423 422
424 423 changes = Change.joins(:changeset).where("#{Changeset.table_name}.repository_id = ?", id).select("committer, user_id, count(*) as count").group("committer, user_id")
425 424
426 425 user_ids = changesets.map(&:user_id).compact.uniq
427 426 authors_names = User.where(:id => user_ids).inject({}) do |memo, user|
428 427 memo[user.id] = user.to_s
429 428 memo
430 429 end
431 430
432 431 (commits + changes).inject({}) do |hash, element|
433 432 mapped_name = element.committer
434 433 if username = authors_names[element.user_id.to_i]
435 434 mapped_name = username
436 435 end
437 436 hash[mapped_name] ||= { :commits_count => 0, :changes_count => 0 }
438 437 if element.is_a?(Changeset)
439 438 hash[mapped_name][:commits_count] += element.count.to_i
440 439 else
441 440 hash[mapped_name][:changes_count] += element.count.to_i
442 441 end
443 442 hash
444 443 end
445 444 end
446 445
447 446 # Returns a scope of changesets that come from the same commit as the given changeset
448 447 # in different repositories that point to the same backend
449 448 def same_commits_in_scope(scope, changeset)
450 449 scope = scope.joins(:repository).where(:repositories => {:url => url, :root_url => root_url, :type => type})
451 450 if changeset.scmid.present?
452 451 scope = scope.where(:scmid => changeset.scmid)
453 452 else
454 453 scope = scope.where(:revision => changeset.revision)
455 454 end
456 455 scope
457 456 end
458 457
459 458 protected
460 459
461 460 # Validates repository url based against an optional regular expression
462 461 # that can be set in the Redmine configuration file.
463 462 def validate_repository_path(attribute=:url)
464 463 regexp = Redmine::Configuration["scm_#{scm_name.to_s.downcase}_path_regexp"]
465 464 if changes[attribute] && regexp.present?
466 465 regexp = regexp.to_s.strip.gsub('%project%') {Regexp.escape(project.try(:identifier).to_s)}
467 466 unless send(attribute).to_s.match(Regexp.new("\\A#{regexp}\\z"))
468 467 errors.add(attribute, :invalid)
469 468 end
470 469 end
471 470 end
472 471
473 472 def check_default
474 473 if !is_default? && set_as_default?
475 474 self.is_default = true
476 475 end
477 476 if is_default? && is_default_changed?
478 477 Repository.where(["project_id = ?", project_id]).update_all(["is_default = ?", false])
479 478 end
480 479 end
481 480
482 481 def load_entries_changesets(entries)
483 482 if entries
484 483 entries.each do |entry|
485 484 if entry.lastrev && entry.lastrev.identifier
486 485 entry.changeset = find_changeset_by_name(entry.lastrev.identifier)
487 486 end
488 487 end
489 488 end
490 489 end
491 490
492 491 private
493 492
494 493 # Deletes repository data
495 494 def clear_changesets
496 495 cs = Changeset.table_name
497 496 ch = Change.table_name
498 497 ci = "#{table_name_prefix}changesets_issues#{table_name_suffix}"
499 498 cp = "#{table_name_prefix}changeset_parents#{table_name_suffix}"
500 499
501 500 self.class.connection.delete("DELETE FROM #{ch} WHERE #{ch}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
502 501 self.class.connection.delete("DELETE FROM #{ci} WHERE #{ci}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
503 502 self.class.connection.delete("DELETE FROM #{cp} WHERE #{cp}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
504 503 self.class.connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}")
505 504 clear_extra_info_of_changesets
506 505 end
507 506
508 507 def clear_extra_info_of_changesets
509 508 end
510 509 end
@@ -1,592 +1,621
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2015 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".force_encoding('UTF-8')
32 32 CHAR_1_HEX = "\xc3\x9c".force_encoding('UTF-8')
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 end
51 51
52 def test_nondefault_repo_with_blank_identifier_destruction
53 repo1 = Repository::Git.new(
54 :project => @project,
55 :url => REPOSITORY_PATH,
56 :identifier => '',
57 :is_default => true
58 )
59 assert repo1.save
60 repo1.fetch_changesets
61
62 repo2 = Repository::Git.new(
63 :project => @project,
64 :url => REPOSITORY_PATH,
65 :identifier => 'repo2',
66 :is_default => true
67 )
68 assert repo2.save
69 repo2.fetch_changesets
70
71 repo1.reload
72 repo2.reload
73 assert !repo1.is_default?
74 assert repo2.is_default?
75
76 assert_difference 'Repository.count', -1 do
77 repo1.destroy
78 end
79 end
80
52 81 def test_blank_path_to_repository_error_message
53 82 set_language_if_valid 'en'
54 83 repo = Repository::Git.new(
55 84 :project => @project,
56 85 :identifier => 'test'
57 86 )
58 87 assert !repo.save
59 88 assert_include "Path to repository cannot be blank",
60 89 repo.errors.full_messages
61 90 end
62 91
63 92 def test_blank_path_to_repository_error_message_fr
64 93 set_language_if_valid 'fr'
65 94 str = "Chemin du d\xc3\xa9p\xc3\xb4t doit \xc3\xaatre renseign\xc3\xa9(e)".force_encoding('UTF-8')
66 95 repo = Repository::Git.new(
67 96 :project => @project,
68 97 :url => "",
69 98 :identifier => 'test',
70 99 :path_encoding => ''
71 100 )
72 101 assert !repo.save
73 102 assert_include str, repo.errors.full_messages
74 103 end
75 104
76 105 if File.directory?(REPOSITORY_PATH)
77 106 ## Ruby uses ANSI api to fork a process on Windows.
78 107 ## Japanese Shift_JIS and Traditional Chinese Big5 have 0x5c(backslash) problem
79 108 ## and these are incompatible with ASCII.
80 109 ## Git for Windows (msysGit) changed internal API from ANSI to Unicode in 1.7.10
81 110 ## http://code.google.com/p/msysgit/issues/detail?id=80
82 111 ## So, Latin-1 path tests fail on Japanese Windows
83 112 WINDOWS_PASS = (Redmine::Platform.mswin? &&
84 113 Redmine::Scm::Adapters::GitAdapter.client_version_above?([1, 7, 10]))
85 114 WINDOWS_SKIP_STR = "TODO: This test fails in Git for Windows above 1.7.10"
86 115
87 116 def test_scm_available
88 117 klass = Repository::Git
89 118 assert_equal "Git", klass.scm_name
90 119 assert klass.scm_adapter_class
91 120 assert_not_equal "", klass.scm_command
92 121 assert_equal true, klass.scm_available
93 122 end
94 123
95 124 def test_entries
96 125 entries = @repository.entries
97 126 assert_kind_of Redmine::Scm::Adapters::Entries, entries
98 127 end
99 128
100 129 def test_fetch_changesets_from_scratch
101 130 assert_nil @repository.extra_info
102 131
103 132 assert_equal 0, @repository.changesets.count
104 133 @repository.fetch_changesets
105 134 @project.reload
106 135
107 136 assert_equal NUM_REV, @repository.changesets.count
108 137 assert_equal 39, @repository.filechanges.count
109 138
110 139 commit = @repository.changesets.find_by_revision("7234cb2750b63f47bff735edc50a1c0a433c2518")
111 140 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518", commit.scmid
112 141 assert_equal "Initial import.\nThe repository contains 3 files.", commit.comments
113 142 assert_equal "jsmith <jsmith@foo.bar>", commit.committer
114 143 assert_equal User.find_by_login('jsmith'), commit.user
115 144 # TODO: add a commit with commit time <> author time to the test repository
116 145 assert_equal Time.gm(2007, 12, 14, 9, 22, 52), commit.committed_on
117 146 assert_equal "2007-12-14".to_date, commit.commit_date
118 147 assert_equal 3, commit.filechanges.count
119 148 change = commit.filechanges.sort_by(&:path).first
120 149 assert_equal "README", change.path
121 150 assert_equal nil, change.from_path
122 151 assert_equal "A", change.action
123 152
124 153 assert_equal NUM_HEAD, @repository.extra_info["heads"].size
125 154 end
126 155
127 156 def test_fetch_changesets_incremental
128 157 assert_equal 0, @repository.changesets.count
129 158 @repository.fetch_changesets
130 159 @project.reload
131 160 assert_equal NUM_REV, @repository.changesets.count
132 161 extra_info_heads = @repository.extra_info["heads"].dup
133 162 assert_equal NUM_HEAD, extra_info_heads.size
134 163 extra_info_heads.delete_if { |x| x == "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c" }
135 164 assert_equal 4, extra_info_heads.size
136 165
137 166 del_revs = [
138 167 "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c",
139 168 "ed5bb786bbda2dee66a2d50faf51429dbc043a7b",
140 169 "4f26664364207fa8b1af9f8722647ab2d4ac5d43",
141 170 "deff712f05a90d96edbd70facc47d944be5897e3",
142 171 "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf",
143 172 "7e61ac704deecde634b51e59daa8110435dcb3da",
144 173 ]
145 174 @repository.changesets.each do |rev|
146 175 rev.destroy if del_revs.detect {|r| r == rev.scmid.to_s }
147 176 end
148 177 @project.reload
149 178 cs1 = @repository.changesets
150 179 assert_equal NUM_REV - 6, cs1.count
151 180 extra_info_heads << "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8"
152 181 h = {}
153 182 h["heads"] = extra_info_heads
154 183 @repository.merge_extra_info(h)
155 184 @repository.save
156 185 @project.reload
157 186 assert @repository.extra_info["heads"].index("4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8")
158 187 @repository.fetch_changesets
159 188 @project.reload
160 189 assert_equal NUM_REV, @repository.changesets.count
161 190 assert_equal NUM_HEAD, @repository.extra_info["heads"].size
162 191 assert @repository.extra_info["heads"].index("83ca5fd546063a3c7dc2e568ba3355661a9e2b2c")
163 192 end
164 193
165 194 def test_fetch_changesets_history_editing
166 195 assert_equal 0, @repository.changesets.count
167 196 @repository.fetch_changesets
168 197 @project.reload
169 198 assert_equal NUM_REV, @repository.changesets.count
170 199 extra_info_heads = @repository.extra_info["heads"].dup
171 200 assert_equal NUM_HEAD, extra_info_heads.size
172 201 extra_info_heads.delete_if { |x| x == "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c" }
173 202 assert_equal 4, extra_info_heads.size
174 203
175 204 del_revs = [
176 205 "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c",
177 206 "ed5bb786bbda2dee66a2d50faf51429dbc043a7b",
178 207 "4f26664364207fa8b1af9f8722647ab2d4ac5d43",
179 208 "deff712f05a90d96edbd70facc47d944be5897e3",
180 209 "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf",
181 210 "7e61ac704deecde634b51e59daa8110435dcb3da",
182 211 ]
183 212 @repository.changesets.each do |rev|
184 213 rev.destroy if del_revs.detect {|r| r == rev.scmid.to_s }
185 214 end
186 215 @project.reload
187 216 assert_equal NUM_REV - 6, @repository.changesets.count
188 217
189 218 c = Changeset.new(:repository => @repository,
190 219 :committed_on => Time.now,
191 220 :revision => "abcd1234efgh",
192 221 :scmid => "abcd1234efgh",
193 222 :comments => 'test')
194 223 assert c.save
195 224 @project.reload
196 225 assert_equal NUM_REV - 5, @repository.changesets.count
197 226
198 227 extra_info_heads << "1234abcd5678"
199 228 h = {}
200 229 h["heads"] = extra_info_heads
201 230 @repository.merge_extra_info(h)
202 231 @repository.save
203 232 @project.reload
204 233 h1 = @repository.extra_info["heads"].dup
205 234 assert h1.index("1234abcd5678")
206 235 assert_equal 5, h1.size
207 236
208 237 @repository.fetch_changesets
209 238 @project.reload
210 239 assert_equal NUM_REV - 5, @repository.changesets.count
211 240 h2 = @repository.extra_info["heads"].dup
212 241 assert_equal h1, h2
213 242 end
214 243
215 244 def test_keep_extra_report_last_commit_in_clear_changesets
216 245 assert_nil @repository.extra_info
217 246 h = {}
218 247 h["extra_report_last_commit"] = "1"
219 248 @repository.merge_extra_info(h)
220 249 @repository.save
221 250 @project.reload
222 251
223 252 assert_equal 0, @repository.changesets.count
224 253 @repository.fetch_changesets
225 254 @project.reload
226 255
227 256 assert_equal NUM_REV, @repository.changesets.count
228 257 @repository.send(:clear_changesets)
229 258 assert_equal 1, @repository.extra_info.size
230 259 assert_equal "1", @repository.extra_info["extra_report_last_commit"]
231 260 end
232 261
233 262 def test_refetch_after_clear_changesets
234 263 assert_nil @repository.extra_info
235 264 assert_equal 0, @repository.changesets.count
236 265 @repository.fetch_changesets
237 266 @project.reload
238 267 assert_equal NUM_REV, @repository.changesets.count
239 268
240 269 @repository.send(:clear_changesets)
241 270 @project.reload
242 271 assert_equal 0, @repository.changesets.count
243 272
244 273 @repository.fetch_changesets
245 274 @project.reload
246 275 assert_equal NUM_REV, @repository.changesets.count
247 276 end
248 277
249 278 def test_parents
250 279 assert_equal 0, @repository.changesets.count
251 280 @repository.fetch_changesets
252 281 @project.reload
253 282 assert_equal NUM_REV, @repository.changesets.count
254 283 r1 = @repository.find_changeset_by_name("7234cb2750b63")
255 284 assert_equal [], r1.parents
256 285 r2 = @repository.find_changeset_by_name("899a15dba03a3")
257 286 assert_equal 1, r2.parents.length
258 287 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
259 288 r2.parents[0].identifier
260 289 r3 = @repository.find_changeset_by_name("32ae898b720c2")
261 290 assert_equal 2, r3.parents.length
262 291 r4 = [r3.parents[0].identifier, r3.parents[1].identifier].sort
263 292 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", r4[0]
264 293 assert_equal "7e61ac704deecde634b51e59daa8110435dcb3da", r4[1]
265 294 end
266 295
267 296 def test_db_consistent_ordering_init
268 297 assert_nil @repository.extra_info
269 298 assert_equal 0, @repository.changesets.count
270 299 @repository.fetch_changesets
271 300 @project.reload
272 301 assert_equal 1, @repository.extra_info["db_consistent"]["ordering"]
273 302 end
274 303
275 304 def test_db_consistent_ordering_before_1_2
276 305 assert_nil @repository.extra_info
277 306 assert_equal 0, @repository.changesets.count
278 307 @repository.fetch_changesets
279 308 @project.reload
280 309 assert_equal NUM_REV, @repository.changesets.count
281 310 assert_not_nil @repository.extra_info
282 311 h = {}
283 312 h["heads"] = []
284 313 h["branches"] = {}
285 314 h["db_consistent"] = {}
286 315 @repository.merge_extra_info(h)
287 316 @repository.save
288 317 assert_equal NUM_REV, @repository.changesets.count
289 318 @repository.fetch_changesets
290 319 @project.reload
291 320 assert_equal 0, @repository.extra_info["db_consistent"]["ordering"]
292 321
293 322 extra_info_heads = @repository.extra_info["heads"].dup
294 323 extra_info_heads.delete_if { |x| x == "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c" }
295 324 del_revs = [
296 325 "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c",
297 326 "ed5bb786bbda2dee66a2d50faf51429dbc043a7b",
298 327 "4f26664364207fa8b1af9f8722647ab2d4ac5d43",
299 328 "deff712f05a90d96edbd70facc47d944be5897e3",
300 329 "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf",
301 330 "7e61ac704deecde634b51e59daa8110435dcb3da",
302 331 ]
303 332 @repository.changesets.each do |rev|
304 333 rev.destroy if del_revs.detect {|r| r == rev.scmid.to_s }
305 334 end
306 335 @project.reload
307 336 cs1 = @repository.changesets
308 337 assert_equal NUM_REV - 6, cs1.count
309 338 assert_equal 0, @repository.extra_info["db_consistent"]["ordering"]
310 339
311 340 extra_info_heads << "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8"
312 341 h = {}
313 342 h["heads"] = extra_info_heads
314 343 @repository.merge_extra_info(h)
315 344 @repository.save
316 345 @project.reload
317 346 assert @repository.extra_info["heads"].index("4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8")
318 347 @repository.fetch_changesets
319 348 @project.reload
320 349 assert_equal NUM_REV, @repository.changesets.count
321 350 assert_equal NUM_HEAD, @repository.extra_info["heads"].size
322 351
323 352 assert_equal 0, @repository.extra_info["db_consistent"]["ordering"]
324 353 end
325 354
326 355 def test_heads_from_branches_hash
327 356 assert_nil @repository.extra_info
328 357 assert_equal 0, @repository.changesets.count
329 358 assert_equal [], @repository.heads_from_branches_hash
330 359 h = {}
331 360 h["branches"] = {}
332 361 h["branches"]["test1"] = {}
333 362 h["branches"]["test1"]["last_scmid"] = "1234abcd"
334 363 h["branches"]["test2"] = {}
335 364 h["branches"]["test2"]["last_scmid"] = "abcd1234"
336 365 @repository.merge_extra_info(h)
337 366 @repository.save
338 367 @project.reload
339 368 assert_equal ["1234abcd", "abcd1234"], @repository.heads_from_branches_hash.sort
340 369 end
341 370
342 371 def test_latest_changesets
343 372 assert_equal 0, @repository.changesets.count
344 373 @repository.fetch_changesets
345 374 @project.reload
346 375 assert_equal NUM_REV, @repository.changesets.count
347 376 # with limit
348 377 changesets = @repository.latest_changesets('', 'master', 2)
349 378 assert_equal 2, changesets.size
350 379
351 380 # with path
352 381 changesets = @repository.latest_changesets('images', 'master')
353 382 assert_equal [
354 383 'deff712f05a90d96edbd70facc47d944be5897e3',
355 384 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
356 385 '7234cb2750b63f47bff735edc50a1c0a433c2518',
357 386 ], changesets.collect(&:revision)
358 387
359 388 changesets = @repository.latest_changesets('README', nil)
360 389 assert_equal [
361 390 '32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf',
362 391 '4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8',
363 392 '713f4944648826f558cf548222f813dabe7cbb04',
364 393 '61b685fbe55ab05b5ac68402d5720c1a6ac973d1',
365 394 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
366 395 '7234cb2750b63f47bff735edc50a1c0a433c2518',
367 396 ], changesets.collect(&:revision)
368 397
369 398 # with path, revision and limit
370 399 changesets = @repository.latest_changesets('images', '899a15dba')
371 400 assert_equal [
372 401 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
373 402 '7234cb2750b63f47bff735edc50a1c0a433c2518',
374 403 ], changesets.collect(&:revision)
375 404
376 405 changesets = @repository.latest_changesets('images', '899a15dba', 1)
377 406 assert_equal [
378 407 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
379 408 ], changesets.collect(&:revision)
380 409
381 410 changesets = @repository.latest_changesets('README', '899a15dba')
382 411 assert_equal [
383 412 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
384 413 '7234cb2750b63f47bff735edc50a1c0a433c2518',
385 414 ], changesets.collect(&:revision)
386 415
387 416 changesets = @repository.latest_changesets('README', '899a15dba', 1)
388 417 assert_equal [
389 418 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
390 419 ], changesets.collect(&:revision)
391 420
392 421 # with path, tag and limit
393 422 changesets = @repository.latest_changesets('images', 'tag01.annotated')
394 423 assert_equal [
395 424 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
396 425 '7234cb2750b63f47bff735edc50a1c0a433c2518',
397 426 ], changesets.collect(&:revision)
398 427
399 428 changesets = @repository.latest_changesets('images', 'tag01.annotated', 1)
400 429 assert_equal [
401 430 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
402 431 ], changesets.collect(&:revision)
403 432
404 433 changesets = @repository.latest_changesets('README', 'tag01.annotated')
405 434 assert_equal [
406 435 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
407 436 '7234cb2750b63f47bff735edc50a1c0a433c2518',
408 437 ], changesets.collect(&:revision)
409 438
410 439 changesets = @repository.latest_changesets('README', 'tag01.annotated', 1)
411 440 assert_equal [
412 441 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
413 442 ], changesets.collect(&:revision)
414 443
415 444 # with path, branch and limit
416 445 changesets = @repository.latest_changesets('images', 'test_branch')
417 446 assert_equal [
418 447 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
419 448 '7234cb2750b63f47bff735edc50a1c0a433c2518',
420 449 ], changesets.collect(&:revision)
421 450
422 451 changesets = @repository.latest_changesets('images', 'test_branch', 1)
423 452 assert_equal [
424 453 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
425 454 ], changesets.collect(&:revision)
426 455
427 456 changesets = @repository.latest_changesets('README', 'test_branch')
428 457 assert_equal [
429 458 '713f4944648826f558cf548222f813dabe7cbb04',
430 459 '61b685fbe55ab05b5ac68402d5720c1a6ac973d1',
431 460 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
432 461 '7234cb2750b63f47bff735edc50a1c0a433c2518',
433 462 ], changesets.collect(&:revision)
434 463
435 464 changesets = @repository.latest_changesets('README', 'test_branch', 2)
436 465 assert_equal [
437 466 '713f4944648826f558cf548222f813dabe7cbb04',
438 467 '61b685fbe55ab05b5ac68402d5720c1a6ac973d1',
439 468 ], changesets.collect(&:revision)
440 469
441 470 if WINDOWS_PASS
442 471 puts WINDOWS_SKIP_STR
443 472 elsif JRUBY_SKIP
444 473 puts JRUBY_SKIP_STR
445 474 else
446 475 # latin-1 encoding path
447 476 changesets = @repository.latest_changesets(
448 477 "latin-1-dir/test-#{CHAR_1_HEX}-2.txt", '64f1f3e89')
449 478 assert_equal [
450 479 '64f1f3e89ad1cb57976ff0ad99a107012ba3481d',
451 480 '4fc55c43bf3d3dc2efb66145365ddc17639ce81e',
452 481 ], changesets.collect(&:revision)
453 482
454 483 changesets = @repository.latest_changesets(
455 484 "latin-1-dir/test-#{CHAR_1_HEX}-2.txt", '64f1f3e89', 1)
456 485 assert_equal [
457 486 '64f1f3e89ad1cb57976ff0ad99a107012ba3481d',
458 487 ], changesets.collect(&:revision)
459 488 end
460 489 end
461 490
462 491 def test_latest_changesets_latin_1_dir
463 492 if WINDOWS_PASS
464 493 puts WINDOWS_SKIP_STR
465 494 elsif JRUBY_SKIP
466 495 puts JRUBY_SKIP_STR
467 496 else
468 497 assert_equal 0, @repository.changesets.count
469 498 @repository.fetch_changesets
470 499 @project.reload
471 500 assert_equal NUM_REV, @repository.changesets.count
472 501 changesets = @repository.latest_changesets(
473 502 "latin-1-dir/test-#{CHAR_1_HEX}-subdir", '1ca7f5ed')
474 503 assert_equal [
475 504 '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127',
476 505 ], changesets.collect(&:revision)
477 506 end
478 507 end
479 508
480 509 def test_find_changeset_by_name
481 510 assert_equal 0, @repository.changesets.count
482 511 @repository.fetch_changesets
483 512 @project.reload
484 513 assert_equal NUM_REV, @repository.changesets.count
485 514 ['7234cb2750b63f47bff735edc50a1c0a433c2518', '7234cb2750b'].each do |r|
486 515 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518',
487 516 @repository.find_changeset_by_name(r).revision
488 517 end
489 518 end
490 519
491 520 def test_find_changeset_by_empty_name
492 521 assert_equal 0, @repository.changesets.count
493 522 @repository.fetch_changesets
494 523 @project.reload
495 524 assert_equal NUM_REV, @repository.changesets.count
496 525 ['', ' ', nil].each do |r|
497 526 assert_nil @repository.find_changeset_by_name(r)
498 527 end
499 528 end
500 529
501 530 def test_identifier
502 531 assert_equal 0, @repository.changesets.count
503 532 @repository.fetch_changesets
504 533 @project.reload
505 534 assert_equal NUM_REV, @repository.changesets.count
506 535 c = @repository.changesets.find_by_revision(
507 536 '7234cb2750b63f47bff735edc50a1c0a433c2518')
508 537 assert_equal c.scmid, c.identifier
509 538 end
510 539
511 540 def test_format_identifier
512 541 assert_equal 0, @repository.changesets.count
513 542 @repository.fetch_changesets
514 543 @project.reload
515 544 assert_equal NUM_REV, @repository.changesets.count
516 545 c = @repository.changesets.find_by_revision(
517 546 '7234cb2750b63f47bff735edc50a1c0a433c2518')
518 547 assert_equal '7234cb27', c.format_identifier
519 548 end
520 549
521 550 def test_activities
522 551 c = Changeset.new(:repository => @repository,
523 552 :committed_on => Time.now,
524 553 :revision => 'abc7234cb2750b63f47bff735edc50a1c0a433c2',
525 554 :scmid => 'abc7234cb2750b63f47bff735edc50a1c0a433c2',
526 555 :comments => 'test')
527 556 assert c.event_title.include?('abc7234c:')
528 557 assert_equal 'abc7234cb2750b63f47bff735edc50a1c0a433c2', c.event_url[:rev]
529 558 end
530 559
531 560 def test_log_utf8
532 561 assert_equal 0, @repository.changesets.count
533 562 @repository.fetch_changesets
534 563 @project.reload
535 564 assert_equal NUM_REV, @repository.changesets.count
536 565 c = @repository.changesets.find_by_revision(
537 566 'ed5bb786bbda2dee66a2d50faf51429dbc043a7b')
538 567 assert_equal "#{FELIX_HEX} <felix@fachschaften.org>", c.committer
539 568 end
540 569
541 570 def test_previous
542 571 assert_equal 0, @repository.changesets.count
543 572 @repository.fetch_changesets
544 573 @project.reload
545 574 assert_equal NUM_REV, @repository.changesets.count
546 575 %w|1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127 1ca7f5ed|.each do |r1|
547 576 changeset = @repository.find_changeset_by_name(r1)
548 577 %w|64f1f3e89ad1cb57976ff0ad99a107012ba3481d 64f1f3e89ad1|.each do |r2|
549 578 assert_equal @repository.find_changeset_by_name(r2), changeset.previous
550 579 end
551 580 end
552 581 end
553 582
554 583 def test_previous_nil
555 584 assert_equal 0, @repository.changesets.count
556 585 @repository.fetch_changesets
557 586 @project.reload
558 587 assert_equal NUM_REV, @repository.changesets.count
559 588 %w|7234cb2750b63f47bff735edc50a1c0a433c2518 7234cb275|.each do |r1|
560 589 changeset = @repository.find_changeset_by_name(r1)
561 590 assert_nil changeset.previous
562 591 end
563 592 end
564 593
565 594 def test_next
566 595 assert_equal 0, @repository.changesets.count
567 596 @repository.fetch_changesets
568 597 @project.reload
569 598 assert_equal NUM_REV, @repository.changesets.count
570 599 %w|64f1f3e89ad1cb57976ff0ad99a107012ba3481d 64f1f3e89ad1|.each do |r2|
571 600 changeset = @repository.find_changeset_by_name(r2)
572 601 %w|1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127 1ca7f5ed|.each do |r1|
573 602 assert_equal @repository.find_changeset_by_name(r1), changeset.next
574 603 end
575 604 end
576 605 end
577 606
578 607 def test_next_nil
579 608 assert_equal 0, @repository.changesets.count
580 609 @repository.fetch_changesets
581 610 @project.reload
582 611 assert_equal NUM_REV, @repository.changesets.count
583 612 %w|2a682156a3b6e77a8bf9cd4590e8db757f3c6c78 2a682156a3b6e77a|.each do |r1|
584 613 changeset = @repository.find_changeset_by_name(r1)
585 614 assert_nil changeset.next
586 615 end
587 616 end
588 617 else
589 618 puts "Git test repository NOT FOUND. Skipping unit tests !!!"
590 619 def test_fake; assert true end
591 620 end
592 621 end
General Comments 0
You need to be logged in to leave comments. Login now