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