##// END OF EJS Templates
Merged r5091 from trunk....
Toshi MARUYAMA -
r4972:891ed84fe39e
parent child
Show More
@@ -1,105 +1,105
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require 'redmine/scm/adapters/mercurial_adapter'
18 require 'redmine/scm/adapters/mercurial_adapter'
19
19
20 class Repository::Mercurial < Repository
20 class Repository::Mercurial < Repository
21 # sort changesets by revision number
21 # sort changesets by revision number
22 has_many :changesets, :order => "#{Changeset.table_name}.id DESC", :foreign_key => 'repository_id'
22 has_many :changesets, :order => "#{Changeset.table_name}.id DESC", :foreign_key => 'repository_id'
23
23
24 attr_protected :root_url
24 attr_protected :root_url
25 validates_presence_of :url
25 validates_presence_of :url
26
26
27 def scm_adapter
27 def scm_adapter
28 Redmine::Scm::Adapters::MercurialAdapter
28 Redmine::Scm::Adapters::MercurialAdapter
29 end
29 end
30
30
31 def self.scm_name
31 def self.scm_name
32 'Mercurial'
32 'Mercurial'
33 end
33 end
34
34
35 def entries(path=nil, identifier=nil)
35 def entries(path=nil, identifier=nil)
36 entries=scm.entries(path, identifier)
36 entries=scm.entries(path, identifier)
37 if entries
37 if entries
38 entries.each do |entry|
38 entries.each do |entry|
39 next unless entry.is_file?
39 next unless entry.is_file?
40 # Set the filesize unless browsing a specific revision
40 # Set the filesize unless browsing a specific revision
41 if identifier.nil?
41 if identifier.nil?
42 full_path = File.join(root_url, entry.path)
42 full_path = File.join(root_url, entry.path)
43 entry.size = File.stat(full_path).size if File.file?(full_path)
43 entry.size = File.stat(full_path).size if File.file?(full_path)
44 end
44 end
45 # Search the DB for the entry's last change
45 # Search the DB for the entry's last change
46 change = changes.find(:first, :conditions => ["path = ?", scm.with_leading_slash(entry.path)], :order => "#{Changeset.table_name}.committed_on DESC")
46 change = changes.find(:first, :conditions => ["path = ?", scm.with_leading_slash(entry.path)], :order => "#{Changeset.table_name}.committed_on DESC")
47 if change
47 if change
48 entry.lastrev.identifier = change.changeset.revision
48 entry.lastrev.identifier = change.changeset.revision
49 entry.lastrev.name = change.changeset.revision
49 entry.lastrev.name = change.changeset.revision
50 entry.lastrev.author = change.changeset.committer
50 entry.lastrev.author = change.changeset.committer
51 entry.lastrev.revision = change.revision
51 entry.lastrev.revision = change.revision
52 end
52 end
53 end
53 end
54 end
54 end
55 entries
55 entries
56 end
56 end
57
57
58 # Returns the latest changesets for +path+; sorted by revision number
58 # Returns the latest changesets for +path+; sorted by revision number
59 def latest_changesets(path, rev, limit=10)
59 def latest_changesets(path, rev, limit=10)
60 if path.blank?
60 if path.blank?
61 changesets.find(:all, :include => :user, :limit => limit)
61 changesets.find(:all, :include => :user, :limit => limit, :order => "id DESC")
62 else
62 else
63 changes.find(:all, :include => {:changeset => :user},
63 changes.find(:all, :include => {:changeset => :user},
64 :conditions => ["path = ?", path.with_leading_slash],
64 :conditions => ["path = ?", path.with_leading_slash],
65 :order => "#{Changeset.table_name}.id DESC",
65 :order => "#{Changeset.table_name}.id DESC",
66 :limit => limit).collect(&:changeset)
66 :limit => limit).collect(&:changeset)
67 end
67 end
68 end
68 end
69
69
70 def fetch_changesets
70 def fetch_changesets
71 scm_info = scm.info
71 scm_info = scm.info
72 if scm_info
72 if scm_info
73 # latest revision found in database
73 # latest revision found in database
74 db_revision = latest_changeset ? latest_changeset.revision.to_i : -1
74 db_revision = latest_changeset ? latest_changeset.revision.to_i : -1
75 # latest revision in the repository
75 # latest revision in the repository
76 latest_revision = scm_info.lastrev
76 latest_revision = scm_info.lastrev
77 return if latest_revision.nil?
77 return if latest_revision.nil?
78 scm_revision = latest_revision.identifier.to_i
78 scm_revision = latest_revision.identifier.to_i
79 if db_revision < scm_revision
79 if db_revision < scm_revision
80 logger.debug "Fetching changesets for repository #{url}" if logger && logger.debug?
80 logger.debug "Fetching changesets for repository #{url}" if logger && logger.debug?
81 identifier_from = db_revision + 1
81 identifier_from = db_revision + 1
82 while (identifier_from <= scm_revision)
82 while (identifier_from <= scm_revision)
83 # loads changesets by batches of 100
83 # loads changesets by batches of 100
84 identifier_to = [identifier_from + 99, scm_revision].min
84 identifier_to = [identifier_from + 99, scm_revision].min
85 revisions = scm.revisions('', identifier_from, identifier_to, :with_paths => true)
85 revisions = scm.revisions('', identifier_from, identifier_to, :with_paths => true)
86 transaction do
86 transaction do
87 revisions.each do |revision|
87 revisions.each do |revision|
88 changeset = Changeset.create(:repository => self,
88 changeset = Changeset.create(:repository => self,
89 :revision => revision.identifier,
89 :revision => revision.identifier,
90 :scmid => revision.scmid,
90 :scmid => revision.scmid,
91 :committer => revision.author,
91 :committer => revision.author,
92 :committed_on => revision.time,
92 :committed_on => revision.time,
93 :comments => revision.message)
93 :comments => revision.message)
94
94
95 revision.paths.each do |change|
95 revision.paths.each do |change|
96 changeset.create_change(change)
96 changeset.create_change(change)
97 end
97 end
98 end
98 end
99 end unless revisions.nil?
99 end unless revisions.nil?
100 identifier_from = identifier_to + 1
100 identifier_from = identifier_to + 1
101 end
101 end
102 end
102 end
103 end
103 end
104 end
104 end
105 end
105 end
@@ -1,129 +1,130
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class RepositoryMercurialTest < ActiveSupport::TestCase
20 class RepositoryMercurialTest < ActiveSupport::TestCase
21 fixtures :projects
21 fixtures :projects
22
22
23 # No '..' in the repository path
23 # No '..' in the repository path
24 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/mercurial_repository'
24 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/mercurial_repository'
25
25
26 def setup
26 def setup
27 @project = Project.find(1)
27 @project = Project.find(1)
28 assert @repository = Repository::Mercurial.create(:project => @project, :url => REPOSITORY_PATH)
28 assert @repository = Repository::Mercurial.create(:project => @project, :url => REPOSITORY_PATH)
29 end
29 end
30
30
31 if File.directory?(REPOSITORY_PATH)
31 if File.directory?(REPOSITORY_PATH)
32 def test_fetch_changesets_from_scratch
32 def test_fetch_changesets_from_scratch
33 @repository.fetch_changesets
33 @repository.fetch_changesets
34 @repository.reload
34 @repository.reload
35
35
36 assert_equal 17, @repository.changesets.count
36 assert_equal 17, @repository.changesets.count
37 assert_equal 25, @repository.changes.count
37 assert_equal 25, @repository.changes.count
38 assert_equal "Initial import.\nThe repository contains 3 files.", @repository.changesets.find_by_revision('0').comments
38 assert_equal "Initial import.\nThe repository contains 3 files.", @repository.changesets.find_by_revision('0').comments
39 end
39 end
40
40
41 def test_fetch_changesets_incremental
41 def test_fetch_changesets_incremental
42 @repository.fetch_changesets
42 @repository.fetch_changesets
43 # Remove changesets with revision > 2
43 # Remove changesets with revision > 2
44 @repository.changesets.find(:all).each {|c| c.destroy if c.revision.to_i > 2}
44 @repository.changesets.find(:all).each {|c| c.destroy if c.revision.to_i > 2}
45 @repository.reload
45 @repository.reload
46 assert_equal 3, @repository.changesets.count
46 assert_equal 3, @repository.changesets.count
47
47
48 @repository.fetch_changesets
48 @repository.fetch_changesets
49 assert_equal 17, @repository.changesets.count
49 assert_equal 17, @repository.changesets.count
50 end
50 end
51
51
52 def test_entries
52 def test_entries
53 assert_equal 2, @repository.entries("sources", 2).size
53 assert_equal 2, @repository.entries("sources", 2).size
54 assert_equal 1, @repository.entries("sources", 3).size
54 assert_equal 1, @repository.entries("sources", 3).size
55 end
55 end
56
56
57 def test_locate_on_outdated_repository
57 def test_locate_on_outdated_repository
58 assert_equal 1, @repository.entries("images", 0).size
58 assert_equal 1, @repository.entries("images", 0).size
59 assert_equal 2, @repository.entries("images").size
59 assert_equal 2, @repository.entries("images").size
60 assert_equal 2, @repository.entries("images", 2).size
60 assert_equal 2, @repository.entries("images", 2).size
61 end
61 end
62
62
63 def test_isodatesec
63 def test_isodatesec
64 # Template keyword 'isodatesec' supported in Mercurial 1.0 and higher
64 # Template keyword 'isodatesec' supported in Mercurial 1.0 and higher
65 if @repository.scm.class.client_version_above?([1, 0])
65 if @repository.scm.class.client_version_above?([1, 0])
66 @repository.fetch_changesets
66 @repository.fetch_changesets
67 @repository.reload
67 @repository.reload
68 rev0_committed_on = Time.gm(2007, 12, 14, 9, 22, 52)
68 rev0_committed_on = Time.gm(2007, 12, 14, 9, 22, 52)
69 assert_equal @repository.changesets.find_by_revision('0').committed_on, rev0_committed_on
69 assert_equal @repository.changesets.find_by_revision('0').committed_on, rev0_committed_on
70 end
70 end
71 end
71 end
72
72
73 def test_changeset_order_by_revision
73 def test_changeset_order_by_revision
74 @repository.fetch_changesets
74 @repository.fetch_changesets
75 @repository.reload
75 @repository.reload
76
76
77 c0 = @repository.latest_changeset
77 c0 = @repository.latest_changeset
78 c1 = @repository.changesets.find_by_revision('0')
78 c1 = @repository.changesets.find_by_revision('0')
79 # sorted by revision (id), not by date
79 # sorted by revision (id), not by date
80 assert c0.revision.to_i > c1.revision.to_i
80 assert c0.revision.to_i > c1.revision.to_i
81 assert c0.committed_on < c1.committed_on
81 assert c0.committed_on < c1.committed_on
82 end
82 end
83
83
84 def test_latest_changesets
84 def test_latest_changesets
85 @repository.fetch_changesets
85 @repository.fetch_changesets
86 @repository.reload
86 @repository.reload
87
87
88 # with_limit
88 # with_limit
89 changesets = @repository.latest_changesets('', nil, 2)
89 changesets = @repository.latest_changesets('', nil, 2)
90 assert_equal @repository.latest_changesets('', nil)[0, 2], changesets
90 # assert_equal @repository.latest_changesets('', nil)[0, 2], changesets
91 assert_equal %w|16 15|, changesets.collect(&:revision)
91
92
92 # with_filepath
93 # with_filepath
93 changesets = @repository.latest_changesets('/sql_escape/percent%dir/percent%file1.txt', nil)
94 changesets = @repository.latest_changesets('/sql_escape/percent%dir/percent%file1.txt', nil)
94 assert_equal %w|11 10 9|, changesets.collect(&:revision)
95 assert_equal %w|11 10 9|, changesets.collect(&:revision)
95
96
96 changesets = @repository.latest_changesets('/sql_escape/underscore_dir/understrike_file.txt', nil)
97 changesets = @repository.latest_changesets('/sql_escape/underscore_dir/understrike_file.txt', nil)
97 assert_equal %w|12 9|, changesets.collect(&:revision)
98 assert_equal %w|12 9|, changesets.collect(&:revision)
98 end
99 end
99
100
100 def test_copied_files
101 def test_copied_files
101 @repository.fetch_changesets
102 @repository.fetch_changesets
102 @repository.reload
103 @repository.reload
103
104
104 cs1 = @repository.changesets.find_by_revision('13')
105 cs1 = @repository.changesets.find_by_revision('13')
105 assert_not_nil cs1
106 assert_not_nil cs1
106 c1 = cs1.changes.sort_by(&:path)
107 c1 = cs1.changes.sort_by(&:path)
107 assert_equal 2, c1.size
108 assert_equal 2, c1.size
108
109
109 assert_equal 'A', c1[0].action
110 assert_equal 'A', c1[0].action
110 assert_equal '/sql_escape/percent%dir/percentfile1.txt', c1[0].path
111 assert_equal '/sql_escape/percent%dir/percentfile1.txt', c1[0].path
111 assert_equal '/sql_escape/percent%dir/percent%file1.txt', c1[0].from_path
112 assert_equal '/sql_escape/percent%dir/percent%file1.txt', c1[0].from_path
112
113
113 assert_equal 'A', c1[1].action
114 assert_equal 'A', c1[1].action
114 assert_equal '/sql_escape/underscore_dir/understrike-file.txt', c1[1].path
115 assert_equal '/sql_escape/underscore_dir/understrike-file.txt', c1[1].path
115 assert_equal '/sql_escape/underscore_dir/understrike_file.txt', c1[1].from_path
116 assert_equal '/sql_escape/underscore_dir/understrike_file.txt', c1[1].from_path
116
117
117 cs2 = @repository.changesets.find_by_revision('15')
118 cs2 = @repository.changesets.find_by_revision('15')
118 c2 = cs2.changes
119 c2 = cs2.changes
119 assert_equal 1, c2.size
120 assert_equal 1, c2.size
120
121
121 assert_equal 'A', c2[0].action
122 assert_equal 'A', c2[0].action
122 assert_equal '/README (1)[2]&,%.-3_4', c2[0].path
123 assert_equal '/README (1)[2]&,%.-3_4', c2[0].path
123 assert_equal '/README', c2[0].from_path
124 assert_equal '/README', c2[0].from_path
124 end
125 end
125 else
126 else
126 puts "Mercurial test repository NOT FOUND. Skipping unit tests !!!"
127 puts "Mercurial test repository NOT FOUND. Skipping unit tests !!!"
127 def test_fake; assert true end
128 def test_fake; assert true end
128 end
129 end
129 end
130 end
General Comments 0
You need to be logged in to leave comments. Login now