##// END OF EJS Templates
scm: mercurial: accept both of revision and nodeid as changeset id (#3724)....
Toshi MARUYAMA -
r4534:e2df831b537c
parent child
Show More
@@ -1,105 +1,116
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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 'redmine/scm/adapters/mercurial_adapter'
19 19
20 20 class Repository::Mercurial < Repository
21 21 # sort changesets by revision number
22 22 has_many :changesets, :order => "#{Changeset.table_name}.id DESC", :foreign_key => 'repository_id'
23 23
24 24 attr_protected :root_url
25 25 validates_presence_of :url
26 26
27 27 def scm_adapter
28 28 Redmine::Scm::Adapters::MercurialAdapter
29 29 end
30 30
31 31 def self.scm_name
32 32 'Mercurial'
33 33 end
34 34
35 35 def entries(path=nil, identifier=nil)
36 36 entries=scm.entries(path, identifier)
37 37 if entries
38 38 entries.each do |entry|
39 39 next unless entry.is_file?
40 40 # Set the filesize unless browsing a specific revision
41 41 if identifier.nil?
42 42 full_path = File.join(root_url, entry.path)
43 43 entry.size = File.stat(full_path).size if File.file?(full_path)
44 44 end
45 45 # Search the DB for the entry's last change
46 46 change = changes.find(:first, :conditions => ["path = ?", scm.with_leading_slash(entry.path)], :order => "#{Changeset.table_name}.committed_on DESC")
47 47 if change
48 48 entry.lastrev.identifier = change.changeset.revision
49 49 entry.lastrev.name = change.changeset.revision
50 50 entry.lastrev.author = change.changeset.committer
51 51 entry.lastrev.revision = change.revision
52 52 end
53 53 end
54 54 end
55 55 entries
56 56 end
57 57
58 # Finds and returns a revision with a number or the beginning of a hash
59 def find_changeset_by_name(name)
60 if /[^\d]/ =~ name or name.to_s.size > 8
61 e = changesets.find(:first, :conditions => ['scmid = ?', name.to_s])
62 else
63 e = changesets.find(:first, :conditions => ['revision = ?', name.to_s])
64 end
65 return e if e
66 changesets.find(:first, :conditions => ['scmid LIKE ?', "#{name}%"]) # last ditch
67 end
68
58 69 # Returns the latest changesets for +path+; sorted by revision number
59 70 def latest_changesets(path, rev, limit=10)
60 71 if path.blank?
61 72 changesets.find(:all, :include => :user, :limit => limit)
62 73 else
63 74 changes.find(:all, :include => {:changeset => :user},
64 75 :conditions => ["path = ?", path.with_leading_slash],
65 76 :order => "#{Changeset.table_name}.id DESC",
66 77 :limit => limit).collect(&:changeset)
67 78 end
68 79 end
69 80
70 81 def fetch_changesets
71 82 scm_info = scm.info
72 83 if scm_info
73 84 # latest revision found in database
74 85 db_revision = latest_changeset ? latest_changeset.revision.to_i : -1
75 86 # latest revision in the repository
76 87 latest_revision = scm_info.lastrev
77 88 return if latest_revision.nil?
78 89 scm_revision = latest_revision.identifier.to_i
79 90 if db_revision < scm_revision
80 91 logger.debug "Fetching changesets for repository #{url}" if logger && logger.debug?
81 92 identifier_from = db_revision + 1
82 93 while (identifier_from <= scm_revision)
83 94 # loads changesets by batches of 100
84 95 identifier_to = [identifier_from + 99, scm_revision].min
85 96 revisions = scm.revisions('', identifier_from, identifier_to, :with_paths => true)
86 97 transaction do
87 98 revisions.each do |revision|
88 99 changeset = Changeset.create(:repository => self,
89 100 :revision => revision.identifier,
90 101 :scmid => revision.scmid,
91 102 :committer => revision.author,
92 103 :committed_on => revision.time,
93 104 :comments => revision.message)
94 105
95 106 revision.paths.each do |change|
96 107 changeset.create_change(change)
97 108 end
98 109 end
99 110 end unless revisions.nil?
100 111 identifier_from = identifier_to + 1
101 112 end
102 113 end
103 114 end
104 115 end
105 116 end
@@ -1,73 +1,84
1 1 require File.expand_path('../../../../../../test_helper', __FILE__)
2 2 begin
3 3 require 'mocha'
4 4
5 5 class MercurialAdapterTest < ActiveSupport::TestCase
6 6
7 7 TEMPLATES_DIR = Redmine::Scm::Adapters::MercurialAdapter::TEMPLATES_DIR
8 8 TEMPLATE_NAME = Redmine::Scm::Adapters::MercurialAdapter::TEMPLATE_NAME
9 9 TEMPLATE_EXTENSION = Redmine::Scm::Adapters::MercurialAdapter::TEMPLATE_EXTENSION
10 10
11 11 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/mercurial_repository'
12 12
13 13 if File.directory?(REPOSITORY_PATH)
14 14 def setup
15 15 @adapter = Redmine::Scm::Adapters::MercurialAdapter.new(REPOSITORY_PATH)
16 16 end
17 17
18 18 def test_hgversion
19 19 to_test = { "Mercurial Distributed SCM (version 0.9.5)\n" => [0,9,5],
20 20 "Mercurial Distributed SCM (1.0)\n" => [1,0],
21 21 "Mercurial Distributed SCM (1e4ddc9ac9f7+20080325)\n" => nil,
22 22 "Mercurial Distributed SCM (1.0.1+20080525)\n" => [1,0,1],
23 23 "Mercurial Distributed SCM (1916e629a29d)\n" => nil,
24 24 "Mercurial SCM Distribuito (versione 0.9.5)\n" => [0,9,5],
25 25 "(1.6)\n(1.7)\n(1.8)" => [1,6],
26 26 "(1.7.1)\r\n(1.8.1)\r\n(1.9.1)" => [1,7,1]}
27 27
28 28 to_test.each do |s, v|
29 29 test_hgversion_for(s, v)
30 30 end
31 31 end
32 32
33 33 def test_template_path
34 34 to_test = { [0,9,5] => "0.9.5",
35 35 [1,0] => "1.0",
36 36 [] => "1.0",
37 37 [1,0,1] => "1.0",
38 38 [1,7] => "1.0",
39 39 [1,7,1] => "1.0" }
40 40 to_test.each do |v, template|
41 41 test_template_path_for(v, template)
42 42 end
43 43 end
44 44
45 45 def test_cat
46 46 assert @adapter.cat("sources/welcome_controller.rb", 2)
47 47 assert_nil @adapter.cat("sources/welcome_controller.rb")
48 48 end
49 49
50 def test_access_by_nodeid
51 path = 'sources/welcome_controller.rb'
52 assert_equal @adapter.cat(path, 2), @adapter.cat(path, '400bb8672109')
53 end
54
55 def test_access_by_fuzzy_nodeid
56 path = 'sources/welcome_controller.rb'
57 # falls back to nodeid
58 assert_equal @adapter.cat(path, 2), @adapter.cat(path, '400')
59 end
60
50 61 private
51 62
52 63 def test_hgversion_for(hgversion, version)
53 64 @adapter.class.expects(:hgversion_from_command_line).returns(hgversion)
54 65 assert_equal version, @adapter.class.hgversion
55 66 end
56 67
57 68 def test_template_path_for(version, template)
58 69 assert_equal "#{TEMPLATES_DIR}/#{TEMPLATE_NAME}-#{template}.#{TEMPLATE_EXTENSION}",
59 70 @adapter.class.template_path_for(version)
60 71 assert File.exist?(@adapter.class.template_path_for(version))
61 72 end
62 73 else
63 74 puts "Mercurial test repository NOT FOUND. Skipping unit tests !!!"
64 75 def test_fake; assert true end
65 76 end
66 77 end
67 78
68 79 rescue LoadError
69 80 class MercurialMochaFake < ActiveSupport::TestCase
70 81 def test_fake; assert(false, "Requires mocha to run those tests") end
71 82 end
72 83 end
73 84
@@ -1,129 +1,146
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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 RepositoryMercurialTest < ActiveSupport::TestCase
21 21 fixtures :projects
22 22
23 23 # No '..' in the repository path
24 24 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/mercurial_repository'
25 25
26 26 def setup
27 27 @project = Project.find(1)
28 28 assert @repository = Repository::Mercurial.create(:project => @project, :url => REPOSITORY_PATH)
29 29 end
30 30
31 31 if File.directory?(REPOSITORY_PATH)
32 32 def test_fetch_changesets_from_scratch
33 33 @repository.fetch_changesets
34 34 @repository.reload
35 35
36 36 assert_equal 17, @repository.changesets.count
37 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.",
39 @repository.changesets.find_by_revision('0').comments
39 40 end
40 41
41 42 def test_fetch_changesets_incremental
42 43 @repository.fetch_changesets
43 44 # Remove changesets with revision > 2
44 45 @repository.changesets.find(:all).each {|c| c.destroy if c.revision.to_i > 2}
45 46 @repository.reload
46 47 assert_equal 3, @repository.changesets.count
47 48
48 49 @repository.fetch_changesets
49 50 assert_equal 17, @repository.changesets.count
50 51 end
51 52
52 53 def test_entries
53 54 assert_equal 2, @repository.entries("sources", 2).size
55 assert_equal 2, @repository.entries("sources", '400bb8672109').size
54 56 assert_equal 1, @repository.entries("sources", 3).size
57 assert_equal 1, @repository.entries("sources", 'b3a615152df8').size
55 58 end
56 59
57 60 def test_locate_on_outdated_repository
58 61 assert_equal 1, @repository.entries("images", 0).size
59 62 assert_equal 2, @repository.entries("images").size
60 63 assert_equal 2, @repository.entries("images", 2).size
61 64 end
62 65
63 66 def test_isodatesec
64 67 # Template keyword 'isodatesec' supported in Mercurial 1.0 and higher
65 68 if @repository.scm.class.client_version_above?([1, 0])
66 69 @repository.fetch_changesets
67 70 @repository.reload
68 71 rev0_committed_on = Time.gm(2007, 12, 14, 9, 22, 52)
69 72 assert_equal @repository.changesets.find_by_revision('0').committed_on, rev0_committed_on
70 73 end
71 74 end
72 75
73 76 def test_changeset_order_by_revision
74 77 @repository.fetch_changesets
75 78 @repository.reload
76 79
77 80 c0 = @repository.latest_changeset
78 81 c1 = @repository.changesets.find_by_revision('0')
79 82 # sorted by revision (id), not by date
80 83 assert c0.revision.to_i > c1.revision.to_i
81 84 assert c0.committed_on < c1.committed_on
82 85 end
83 86
84 87 def test_latest_changesets
85 88 @repository.fetch_changesets
86 89 @repository.reload
87 90
88 91 # with_limit
89 92 changesets = @repository.latest_changesets('', nil, 2)
90 93 assert_equal @repository.latest_changesets('', nil)[0, 2], changesets
91 94
92 95 # with_filepath
93 96 changesets = @repository.latest_changesets('/sql_escape/percent%dir/percent%file1.txt', nil)
94 97 assert_equal %w|11 10 9|, changesets.collect(&:revision)
95 98
96 99 changesets = @repository.latest_changesets('/sql_escape/underscore_dir/understrike_file.txt', nil)
97 100 assert_equal %w|12 9|, changesets.collect(&:revision)
98 101 end
99 102
100 103 def test_copied_files
101 104 @repository.fetch_changesets
102 105 @repository.reload
103 106
104 107 cs1 = @repository.changesets.find_by_revision('13')
105 108 assert_not_nil cs1
106 109 c1 = cs1.changes
107 110 assert_equal 2, c1.size
108 111
109 112 assert_equal 'A', c1[0].action
110 113 assert_equal '/sql_escape/percent%dir/percentfile1.txt', c1[0].path
111 114 assert_equal '/sql_escape/percent%dir/percent%file1.txt', c1[0].from_path
112 115
113 116 assert_equal 'A', c1[1].action
114 117 assert_equal '/sql_escape/underscore_dir/understrike-file.txt', c1[1].path
115 118 assert_equal '/sql_escape/underscore_dir/understrike_file.txt', c1[1].from_path
116 119
117 120 cs2 = @repository.changesets.find_by_revision('15')
118 121 c2 = cs2.changes
119 122 assert_equal 1, c2.size
120 123
121 124 assert_equal 'A', c2[0].action
122 125 assert_equal '/README (1)[2]&,%.-3_4', c2[0].path
123 126 assert_equal '/README', c2[0].from_path
124 127 end
128
129 def test_find_changeset_by_name
130 @repository.fetch_changesets
131 @repository.reload
132 %w|2 400bb8672109 400|.each do |r|
133 assert_equal @repository.find_changeset_by_name(r).revision, '2'
134 end
135 end
136
137 def test_find_changeset_by_invalid_name
138 @repository.fetch_changesets
139 @repository.reload
140 assert_nil @repository.find_changeset_by_name('100000')
141 end
125 142 else
126 143 puts "Mercurial test repository NOT FOUND. Skipping unit tests !!!"
127 144 def test_fake; assert true end
128 145 end
129 146 end
General Comments 0
You need to be logged in to leave comments. Login now