##// END OF EJS Templates
scm: cvs: generate pseudo scmid for auto issue close text (#6706)....
Toshi MARUYAMA -
r4682:109fd2cdfc88
parent child
Show More
@@ -1,165 +1,170
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/cvs_adapter'
18 require 'redmine/scm/adapters/cvs_adapter'
19 require 'digest/sha1'
19 require 'digest/sha1'
20
20
21 class Repository::Cvs < Repository
21 class Repository::Cvs < Repository
22 validates_presence_of :url, :root_url
22 validates_presence_of :url, :root_url
23
23
24 def scm_adapter
24 def scm_adapter
25 Redmine::Scm::Adapters::CvsAdapter
25 Redmine::Scm::Adapters::CvsAdapter
26 end
26 end
27
27
28 def self.scm_name
28 def self.scm_name
29 'CVS'
29 'CVS'
30 end
30 end
31
31
32 def entry(path=nil, identifier=nil)
32 def entry(path=nil, identifier=nil)
33 rev = identifier.nil? ? nil : changesets.find_by_revision(identifier)
33 rev = identifier.nil? ? nil : changesets.find_by_revision(identifier)
34 scm.entry(path, rev.nil? ? nil : rev.committed_on)
34 scm.entry(path, rev.nil? ? nil : rev.committed_on)
35 end
35 end
36
36
37 def entries(path=nil, identifier=nil)
37 def entries(path=nil, identifier=nil)
38 rev = identifier.nil? ? nil : changesets.find_by_revision(identifier)
38 rev = identifier.nil? ? nil : changesets.find_by_revision(identifier)
39 entries = scm.entries(path, rev.nil? ? nil : rev.committed_on)
39 entries = scm.entries(path, rev.nil? ? nil : rev.committed_on)
40 if entries
40 if entries
41 entries.each() do |entry|
41 entries.each() do |entry|
42 unless entry.lastrev.nil? || entry.lastrev.identifier
42 unless entry.lastrev.nil? || entry.lastrev.identifier
43 change=changes.find_by_revision_and_path( entry.lastrev.revision, scm.with_leading_slash(entry.path) )
43 change=changes.find_by_revision_and_path( entry.lastrev.revision, scm.with_leading_slash(entry.path) )
44 if change
44 if change
45 entry.lastrev.identifier=change.changeset.revision
45 entry.lastrev.identifier=change.changeset.revision
46 entry.lastrev.author=change.changeset.committer
46 entry.lastrev.author=change.changeset.committer
47 entry.lastrev.revision=change.revision
47 entry.lastrev.revision=change.revision
48 entry.lastrev.branch=change.branch
48 entry.lastrev.branch=change.branch
49 end
49 end
50 end
50 end
51 end
51 end
52 end
52 end
53 entries
53 entries
54 end
54 end
55
55
56 def cat(path, identifier=nil)
56 def cat(path, identifier=nil)
57 rev = identifier.nil? ? nil : changesets.find_by_revision(identifier)
57 rev = identifier.nil? ? nil : changesets.find_by_revision(identifier)
58 scm.cat(path, rev.nil? ? nil : rev.committed_on)
58 scm.cat(path, rev.nil? ? nil : rev.committed_on)
59 end
59 end
60
60
61 def diff(path, rev, rev_to)
61 def diff(path, rev, rev_to)
62 #convert rev to revision. CVS can't handle changesets here
62 #convert rev to revision. CVS can't handle changesets here
63 diff=[]
63 diff=[]
64 changeset_from=changesets.find_by_revision(rev)
64 changeset_from=changesets.find_by_revision(rev)
65 if rev_to.to_i > 0
65 if rev_to.to_i > 0
66 changeset_to=changesets.find_by_revision(rev_to)
66 changeset_to=changesets.find_by_revision(rev_to)
67 end
67 end
68 changeset_from.changes.each() do |change_from|
68 changeset_from.changes.each() do |change_from|
69
69
70 revision_from=nil
70 revision_from=nil
71 revision_to=nil
71 revision_to=nil
72
72
73 revision_from=change_from.revision if path.nil? || (change_from.path.starts_with? scm.with_leading_slash(path))
73 revision_from=change_from.revision if path.nil? || (change_from.path.starts_with? scm.with_leading_slash(path))
74
74
75 if revision_from
75 if revision_from
76 if changeset_to
76 if changeset_to
77 changeset_to.changes.each() do |change_to|
77 changeset_to.changes.each() do |change_to|
78 revision_to=change_to.revision if change_to.path==change_from.path
78 revision_to=change_to.revision if change_to.path==change_from.path
79 end
79 end
80 end
80 end
81 unless revision_to
81 unless revision_to
82 revision_to=scm.get_previous_revision(revision_from)
82 revision_to=scm.get_previous_revision(revision_from)
83 end
83 end
84 file_diff = scm.diff(change_from.path, revision_from, revision_to)
84 file_diff = scm.diff(change_from.path, revision_from, revision_to)
85 diff = diff + file_diff unless file_diff.nil?
85 diff = diff + file_diff unless file_diff.nil?
86 end
86 end
87 end
87 end
88 return diff
88 return diff
89 end
89 end
90
90
91 def fetch_changesets
91 def fetch_changesets
92 # some nifty bits to introduce a commit-id with cvs
92 # some nifty bits to introduce a commit-id with cvs
93 # natively cvs doesn't provide any kind of changesets, there is only a revision per file.
93 # natively cvs doesn't provide any kind of changesets, there is only a revision per file.
94 # we now take a guess using the author, the commitlog and the commit-date.
94 # we now take a guess using the author, the commitlog and the commit-date.
95
95
96 # last one is the next step to take. the commit-date is not equal for all
96 # last one is the next step to take. the commit-date is not equal for all
97 # commits in one changeset. cvs update the commit-date when the *,v file was touched. so
97 # commits in one changeset. cvs update the commit-date when the *,v file was touched. so
98 # we use a small delta here, to merge all changes belonging to _one_ changeset
98 # we use a small delta here, to merge all changes belonging to _one_ changeset
99 time_delta=10.seconds
99 time_delta=10.seconds
100
100
101 fetch_since = latest_changeset ? latest_changeset.committed_on : nil
101 fetch_since = latest_changeset ? latest_changeset.committed_on : nil
102 transaction do
102 transaction do
103 tmp_rev_num = 1
103 tmp_rev_num = 1
104 scm.revisions('', fetch_since, nil, :with_paths => true) do |revision|
104 scm.revisions('', fetch_since, nil, :with_paths => true) do |revision|
105 # only add the change to the database, if it doen't exists. the cvs log
105 # only add the change to the database, if it doen't exists. the cvs log
106 # is not exclusive at all.
106 # is not exclusive at all.
107 tmp_time = revision.time.clone
107 unless changes.find_by_path_and_revision(
108 unless changes.find_by_path_and_revision(
108 scm.with_leading_slash(revision.paths[0][:path]), revision.paths[0][:revision])
109 scm.with_leading_slash(revision.paths[0][:path]), revision.paths[0][:revision])
109 cs = changesets.find(:first, :conditions=>{
110 cs = changesets.find(:first, :conditions=>{
110 :committed_on=>revision.time-time_delta..revision.time+time_delta,
111 :committed_on=>tmp_time - time_delta .. tmp_time + time_delta,
111 :committer=>revision.author,
112 :committer=>revision.author,
112 :comments=>Changeset.normalize_comments(revision.message)
113 :comments=>Changeset.normalize_comments(revision.message)
113 })
114 })
114
115
115 # create a new changeset....
116 # create a new changeset....
116 unless cs
117 unless cs
117 # we use a temporaray revision number here (just for inserting)
118 # we use a temporaray revision number here (just for inserting)
118 # later on, we calculate a continous positive number
119 # later on, we calculate a continous positive number
120 tmp_time2 = tmp_time.clone.gmtime
121 branch = revision.paths[0][:branch]
122 scmid = branch + "-" + tmp_time2.strftime("%Y%m%d-%H%M%S")
119 cs = Changeset.create(:repository => self,
123 cs = Changeset.create(:repository => self,
120 :revision => "tmp#{tmp_rev_num}",
124 :revision => "tmp#{tmp_rev_num}",
125 :scmid => scmid,
121 :committer => revision.author,
126 :committer => revision.author,
122 :committed_on => revision.time,
127 :committed_on => tmp_time,
123 :comments => revision.message)
128 :comments => revision.message)
124 tmp_rev_num += 1
129 tmp_rev_num += 1
125 end
130 end
126
131
127 #convert CVS-File-States to internal Action-abbrevations
132 #convert CVS-File-States to internal Action-abbrevations
128 #default action is (M)odified
133 #default action is (M)odified
129 action="M"
134 action="M"
130 if revision.paths[0][:action]=="Exp" && revision.paths[0][:revision]=="1.1"
135 if revision.paths[0][:action]=="Exp" && revision.paths[0][:revision]=="1.1"
131 action="A" #add-action always at first revision (= 1.1)
136 action="A" #add-action always at first revision (= 1.1)
132 elsif revision.paths[0][:action]=="dead"
137 elsif revision.paths[0][:action]=="dead"
133 action="D" #dead-state is similar to Delete
138 action="D" #dead-state is similar to Delete
134 end
139 end
135
140
136 Change.create(:changeset => cs,
141 Change.create(:changeset => cs,
137 :action => action,
142 :action => action,
138 :path => scm.with_leading_slash(revision.paths[0][:path]),
143 :path => scm.with_leading_slash(revision.paths[0][:path]),
139 :revision => revision.paths[0][:revision],
144 :revision => revision.paths[0][:revision],
140 :branch => revision.paths[0][:branch]
145 :branch => revision.paths[0][:branch]
141 )
146 )
142 end
147 end
143 end
148 end
144
149
145 # Renumber new changesets in chronological order
150 # Renumber new changesets in chronological order
146 changesets.find(
151 changesets.find(
147 :all, :order => 'committed_on ASC, id ASC', :conditions => "revision LIKE 'tmp%'"
152 :all, :order => 'committed_on ASC, id ASC', :conditions => "revision LIKE 'tmp%'"
148 ).each do |changeset|
153 ).each do |changeset|
149 changeset.update_attribute :revision, next_revision_number
154 changeset.update_attribute :revision, next_revision_number
150 end
155 end
151 end # transaction
156 end # transaction
152 @current_revision_number = nil
157 @current_revision_number = nil
153 end
158 end
154
159
155 private
160 private
156
161
157 # Returns the next revision number to assign to a CVS changeset
162 # Returns the next revision number to assign to a CVS changeset
158 def next_revision_number
163 def next_revision_number
159 # Need to retrieve existing revision numbers to sort them as integers
164 # Need to retrieve existing revision numbers to sort them as integers
160 sql = "SELECT revision FROM #{Changeset.table_name} "
165 sql = "SELECT revision FROM #{Changeset.table_name} "
161 sql << "WHERE repository_id = #{id} AND revision NOT LIKE 'tmp%'"
166 sql << "WHERE repository_id = #{id} AND revision NOT LIKE 'tmp%'"
162 @current_revision_number ||= (connection.select_values(sql).collect(&:to_i).max || 0)
167 @current_revision_number ||= (connection.select_values(sql).collect(&:to_i).max || 0)
163 @current_revision_number += 1
168 @current_revision_number += 1
164 end
169 end
165 end
170 end
@@ -1,89 +1,94
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 require 'pp'
19 require 'pp'
20 class RepositoryCvsTest < ActiveSupport::TestCase
20 class RepositoryCvsTest < 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/cvs_repository'
24 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/cvs_repository'
25 REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin?
25 REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin?
26 # CVS module
26 # CVS module
27 MODULE_NAME = 'test'
27 MODULE_NAME = 'test'
28
28
29 def setup
29 def setup
30 @project = Project.find(3)
30 @project = Project.find(3)
31 assert @repository = Repository::Cvs.create(:project => @project,
31 assert @repository = Repository::Cvs.create(:project => @project,
32 :root_url => REPOSITORY_PATH,
32 :root_url => REPOSITORY_PATH,
33 :url => MODULE_NAME)
33 :url => MODULE_NAME)
34 end
34 end
35
35
36 if File.directory?(REPOSITORY_PATH)
36 if File.directory?(REPOSITORY_PATH)
37 def test_fetch_changesets_from_scratch
37 def test_fetch_changesets_from_scratch
38 assert_equal 0, @repository.changesets.count
38 assert_equal 0, @repository.changesets.count
39 @repository.fetch_changesets
39 @repository.fetch_changesets
40 @repository.reload
40 @repository.reload
41
41
42 assert_equal 5, @repository.changesets.count
42 assert_equal 5, @repository.changesets.count
43 assert_equal 14, @repository.changes.count
43 assert_equal 14, @repository.changes.count
44 assert_not_nil @repository.changesets.find_by_comments('Two files changed')
44 assert_not_nil @repository.changesets.find_by_comments('Two files changed')
45
46 r2 = @repository.changesets.find_by_revision('2')
47 assert_equal 'v1-20071213-162510', r2.scmid
45 end
48 end
46
49
47 def test_fetch_changesets_incremental
50 def test_fetch_changesets_incremental
48 assert_equal 0, @repository.changesets.count
51 assert_equal 0, @repository.changesets.count
49 @repository.fetch_changesets
52 @repository.fetch_changesets
50 # Remove changesets with revision > 3
53 # Remove changesets with revision > 3
51 @repository.changesets.find(:all).each {|c| c.destroy if c.revision.to_i > 3}
54 @repository.changesets.find(:all).each {|c| c.destroy if c.revision.to_i > 3}
52 @repository.reload
55 @repository.reload
53 assert_equal 3, @repository.changesets.count
56 assert_equal 3, @repository.changesets.count
54 assert_equal %w|3 2 1|, @repository.changesets.collect(&:revision)
57 assert_equal %w|3 2 1|, @repository.changesets.collect(&:revision)
55
58
56 rev3_commit = @repository.changesets.find(:first, :order => 'committed_on DESC')
59 rev3_commit = @repository.changesets.find(:first, :order => 'committed_on DESC')
57 assert_equal '3', rev3_commit.revision
60 assert_equal '3', rev3_commit.revision
58 # 2007-12-14 01:27:22 +0900
61 # 2007-12-14 01:27:22 +0900
59 rev3_committed_on = Time.gm(2007, 12, 13, 16, 27, 22)
62 rev3_committed_on = Time.gm(2007, 12, 13, 16, 27, 22)
63 assert_equal 'HEAD-20071213-162722', rev3_commit.scmid
60 assert_equal rev3_committed_on, rev3_commit.committed_on
64 assert_equal rev3_committed_on, rev3_commit.committed_on
61 latest_rev = @repository.latest_changeset
65 latest_rev = @repository.latest_changeset
62 assert_equal rev3_committed_on, latest_rev.committed_on
66 assert_equal rev3_committed_on, latest_rev.committed_on
63
67
64 @repository.fetch_changesets
68 @repository.fetch_changesets
65 @repository.reload
69 @repository.reload
66 assert_equal 5, @repository.changesets.count
70 assert_equal 5, @repository.changesets.count
67
71
68 assert_equal %w|5 4 3 2 1|, @repository.changesets.collect(&:revision)
72 assert_equal %w|5 4 3 2 1|, @repository.changesets.collect(&:revision)
69 rev5_commit = @repository.changesets.find(:first, :order => 'committed_on DESC')
73 rev5_commit = @repository.changesets.find(:first, :order => 'committed_on DESC')
74 assert_equal 'HEAD-20071213-163001', rev5_commit.scmid
70 # 2007-12-14 01:30:01 +0900
75 # 2007-12-14 01:30:01 +0900
71 rev5_committed_on = Time.gm(2007, 12, 13, 16, 30, 1)
76 rev5_committed_on = Time.gm(2007, 12, 13, 16, 30, 1)
72 assert_equal rev5_committed_on, rev5_commit.committed_on
77 assert_equal rev5_committed_on, rev5_commit.committed_on
73 end
78 end
74
79
75 def test_deleted_files_should_not_be_listed
80 def test_deleted_files_should_not_be_listed
76 assert_equal 0, @repository.changesets.count
81 assert_equal 0, @repository.changesets.count
77 @repository.fetch_changesets
82 @repository.fetch_changesets
78 @repository.reload
83 @repository.reload
79 assert_equal 5, @repository.changesets.count
84 assert_equal 5, @repository.changesets.count
80
85
81 entries = @repository.entries('sources')
86 entries = @repository.entries('sources')
82 assert entries.detect {|e| e.name == 'watchers_controller.rb'}
87 assert entries.detect {|e| e.name == 'watchers_controller.rb'}
83 assert_nil entries.detect {|e| e.name == 'welcome_controller.rb'}
88 assert_nil entries.detect {|e| e.name == 'welcome_controller.rb'}
84 end
89 end
85 else
90 else
86 puts "CVS test repository NOT FOUND. Skipping unit tests !!!"
91 puts "CVS test repository NOT FOUND. Skipping unit tests !!!"
87 def test_fake; assert true end
92 def test_fake; assert true end
88 end
93 end
89 end
94 end
General Comments 0
You need to be logged in to leave comments. Login now