##// END OF EJS Templates
scm: cvs: override annotate() in model....
Toshi MARUYAMA -
r5290:0a0819f81ee3
parent child
Show More
@@ -1,189 +1,198
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, :log_encoding
22 validates_presence_of :url, :root_url, :log_encoding
23
23
24 ATTRIBUTE_KEY_NAMES = {
24 ATTRIBUTE_KEY_NAMES = {
25 "url" => "CVSROOT",
25 "url" => "CVSROOT",
26 "root_url" => "Module",
26 "root_url" => "Module",
27 "log_encoding" => "Commit messages encoding",
27 "log_encoding" => "Commit messages encoding",
28 }
28 }
29 def self.human_attribute_name(attribute_key_name)
29 def self.human_attribute_name(attribute_key_name)
30 ATTRIBUTE_KEY_NAMES[attribute_key_name] || super
30 ATTRIBUTE_KEY_NAMES[attribute_key_name] || super
31 end
31 end
32
32
33 def self.scm_adapter_class
33 def self.scm_adapter_class
34 Redmine::Scm::Adapters::CvsAdapter
34 Redmine::Scm::Adapters::CvsAdapter
35 end
35 end
36
36
37 def self.scm_name
37 def self.scm_name
38 'CVS'
38 'CVS'
39 end
39 end
40
40
41 def entry(path=nil, identifier=nil)
41 def entry(path=nil, identifier=nil)
42 rev = identifier.nil? ? nil : changesets.find_by_revision(identifier)
42 rev = identifier.nil? ? nil : changesets.find_by_revision(identifier)
43 scm.entry(path, rev.nil? ? nil : rev.committed_on)
43 scm.entry(path, rev.nil? ? nil : rev.committed_on)
44 end
44 end
45
45
46 def entries(path=nil, identifier=nil)
46 def entries(path=nil, identifier=nil)
47 rev = identifier.nil? ? nil : changesets.find_by_revision(identifier)
47 rev = identifier.nil? ? nil : changesets.find_by_revision(identifier)
48 entries = scm.entries(path, rev.nil? ? nil : rev.committed_on)
48 entries = scm.entries(path, rev.nil? ? nil : rev.committed_on)
49 if entries
49 if entries
50 entries.each() do |entry|
50 entries.each() do |entry|
51 if ( ! entry.lastrev.nil? ) && ( ! entry.lastrev.revision.nil? )
51 if ( ! entry.lastrev.nil? ) && ( ! entry.lastrev.revision.nil? )
52 change=changes.find_by_revision_and_path(
52 change=changes.find_by_revision_and_path(
53 entry.lastrev.revision,
53 entry.lastrev.revision,
54 scm.with_leading_slash(entry.path) )
54 scm.with_leading_slash(entry.path) )
55 if change
55 if change
56 entry.lastrev.identifier = change.changeset.revision
56 entry.lastrev.identifier = change.changeset.revision
57 entry.lastrev.revision = change.changeset.revision
57 entry.lastrev.revision = change.changeset.revision
58 entry.lastrev.author = change.changeset.committer
58 entry.lastrev.author = change.changeset.committer
59 # entry.lastrev.branch = change.branch
59 # entry.lastrev.branch = change.branch
60 end
60 end
61 end
61 end
62 end
62 end
63 end
63 end
64 entries
64 entries
65 end
65 end
66
66
67 def cat(path, identifier=nil)
67 def cat(path, identifier=nil)
68 rev = nil
68 rev = nil
69 if ! identifier.nil?
69 if ! identifier.nil?
70 rev = changesets.find_by_revision(identifier)
70 rev = changesets.find_by_revision(identifier)
71 return nil if rev.nil?
71 return nil if rev.nil?
72 end
72 end
73 scm.cat(path, rev.nil? ? nil : rev.committed_on)
73 scm.cat(path, rev.nil? ? nil : rev.committed_on)
74 end
74 end
75
75
76 def annotate(path, identifier=nil)
77 rev = nil
78 if ! identifier.nil?
79 rev = changesets.find_by_revision(identifier)
80 return nil if rev.nil?
81 end
82 scm.annotate(path, rev.nil? ? nil : rev.committed_on)
83 end
84
76 def diff(path, rev, rev_to)
85 def diff(path, rev, rev_to)
77 # convert rev to revision. CVS can't handle changesets here
86 # convert rev to revision. CVS can't handle changesets here
78 diff=[]
87 diff=[]
79 changeset_from = changesets.find_by_revision(rev)
88 changeset_from = changesets.find_by_revision(rev)
80 if rev_to.to_i > 0
89 if rev_to.to_i > 0
81 changeset_to = changesets.find_by_revision(rev_to)
90 changeset_to = changesets.find_by_revision(rev_to)
82 end
91 end
83 changeset_from.changes.each() do |change_from|
92 changeset_from.changes.each() do |change_from|
84 revision_from = nil
93 revision_from = nil
85 revision_to = nil
94 revision_to = nil
86 if path.nil? || (change_from.path.starts_with? scm.with_leading_slash(path))
95 if path.nil? || (change_from.path.starts_with? scm.with_leading_slash(path))
87 revision_from = change_from.revision
96 revision_from = change_from.revision
88 end
97 end
89 if revision_from
98 if revision_from
90 if changeset_to
99 if changeset_to
91 changeset_to.changes.each() do |change_to|
100 changeset_to.changes.each() do |change_to|
92 revision_to=change_to.revision if change_to.path==change_from.path
101 revision_to=change_to.revision if change_to.path==change_from.path
93 end
102 end
94 end
103 end
95 unless revision_to
104 unless revision_to
96 revision_to=scm.get_previous_revision(revision_from)
105 revision_to=scm.get_previous_revision(revision_from)
97 end
106 end
98 file_diff = scm.diff(change_from.path, revision_from, revision_to)
107 file_diff = scm.diff(change_from.path, revision_from, revision_to)
99 diff = diff + file_diff unless file_diff.nil?
108 diff = diff + file_diff unless file_diff.nil?
100 end
109 end
101 end
110 end
102 return diff
111 return diff
103 end
112 end
104
113
105 def fetch_changesets
114 def fetch_changesets
106 # some nifty bits to introduce a commit-id with cvs
115 # some nifty bits to introduce a commit-id with cvs
107 # natively cvs doesn't provide any kind of changesets,
116 # natively cvs doesn't provide any kind of changesets,
108 # there is only a revision per file.
117 # there is only a revision per file.
109 # we now take a guess using the author, the commitlog and the commit-date.
118 # we now take a guess using the author, the commitlog and the commit-date.
110
119
111 # last one is the next step to take. the commit-date is not equal for all
120 # last one is the next step to take. the commit-date is not equal for all
112 # commits in one changeset. cvs update the commit-date when the *,v file was touched. so
121 # commits in one changeset. cvs update the commit-date when the *,v file was touched. so
113 # we use a small delta here, to merge all changes belonging to _one_ changeset
122 # we use a small delta here, to merge all changes belonging to _one_ changeset
114 time_delta=10.seconds
123 time_delta=10.seconds
115
124
116 fetch_since = latest_changeset ? latest_changeset.committed_on : nil
125 fetch_since = latest_changeset ? latest_changeset.committed_on : nil
117 transaction do
126 transaction do
118 tmp_rev_num = 1
127 tmp_rev_num = 1
119 scm.revisions('', fetch_since, nil, :with_paths => true) do |revision|
128 scm.revisions('', fetch_since, nil, :with_paths => true) do |revision|
120 # only add the change to the database, if it doen't exists. the cvs log
129 # only add the change to the database, if it doen't exists. the cvs log
121 # is not exclusive at all.
130 # is not exclusive at all.
122 tmp_time = revision.time.clone
131 tmp_time = revision.time.clone
123 unless changes.find_by_path_and_revision(
132 unless changes.find_by_path_and_revision(
124 scm.with_leading_slash(revision.paths[0][:path]), revision.paths[0][:revision])
133 scm.with_leading_slash(revision.paths[0][:path]), revision.paths[0][:revision])
125 cmt = Changeset.normalize_comments(revision.message, repo_log_encoding)
134 cmt = Changeset.normalize_comments(revision.message, repo_log_encoding)
126 cs = changesets.find(:first, :conditions=>{
135 cs = changesets.find(:first, :conditions=>{
127 :committed_on=>tmp_time - time_delta .. tmp_time + time_delta,
136 :committed_on=>tmp_time - time_delta .. tmp_time + time_delta,
128 :committer=>revision.author,
137 :committer=>revision.author,
129 :comments=>cmt
138 :comments=>cmt
130 })
139 })
131
140
132 # create a new changeset....
141 # create a new changeset....
133 unless cs
142 unless cs
134 # we use a temporaray revision number here (just for inserting)
143 # we use a temporaray revision number here (just for inserting)
135 # later on, we calculate a continous positive number
144 # later on, we calculate a continous positive number
136 tmp_time2 = tmp_time.clone.gmtime
145 tmp_time2 = tmp_time.clone.gmtime
137 branch = revision.paths[0][:branch]
146 branch = revision.paths[0][:branch]
138 scmid = branch + "-" + tmp_time2.strftime("%Y%m%d-%H%M%S")
147 scmid = branch + "-" + tmp_time2.strftime("%Y%m%d-%H%M%S")
139 cs = Changeset.create(:repository => self,
148 cs = Changeset.create(:repository => self,
140 :revision => "tmp#{tmp_rev_num}",
149 :revision => "tmp#{tmp_rev_num}",
141 :scmid => scmid,
150 :scmid => scmid,
142 :committer => revision.author,
151 :committer => revision.author,
143 :committed_on => tmp_time,
152 :committed_on => tmp_time,
144 :comments => revision.message)
153 :comments => revision.message)
145 tmp_rev_num += 1
154 tmp_rev_num += 1
146 end
155 end
147
156
148 #convert CVS-File-States to internal Action-abbrevations
157 #convert CVS-File-States to internal Action-abbrevations
149 #default action is (M)odified
158 #default action is (M)odified
150 action="M"
159 action="M"
151 if revision.paths[0][:action]=="Exp" && revision.paths[0][:revision]=="1.1"
160 if revision.paths[0][:action]=="Exp" && revision.paths[0][:revision]=="1.1"
152 action="A" #add-action always at first revision (= 1.1)
161 action="A" #add-action always at first revision (= 1.1)
153 elsif revision.paths[0][:action]=="dead"
162 elsif revision.paths[0][:action]=="dead"
154 action="D" #dead-state is similar to Delete
163 action="D" #dead-state is similar to Delete
155 end
164 end
156
165
157 Change.create(
166 Change.create(
158 :changeset => cs,
167 :changeset => cs,
159 :action => action,
168 :action => action,
160 :path => scm.with_leading_slash(revision.paths[0][:path]),
169 :path => scm.with_leading_slash(revision.paths[0][:path]),
161 :revision => revision.paths[0][:revision],
170 :revision => revision.paths[0][:revision],
162 :branch => revision.paths[0][:branch]
171 :branch => revision.paths[0][:branch]
163 )
172 )
164 end
173 end
165 end
174 end
166
175
167 # Renumber new changesets in chronological order
176 # Renumber new changesets in chronological order
168 changesets.find(
177 changesets.find(
169 :all,
178 :all,
170 :order => 'committed_on ASC, id ASC',
179 :order => 'committed_on ASC, id ASC',
171 :conditions => "revision LIKE 'tmp%'"
180 :conditions => "revision LIKE 'tmp%'"
172 ).each do |changeset|
181 ).each do |changeset|
173 changeset.update_attribute :revision, next_revision_number
182 changeset.update_attribute :revision, next_revision_number
174 end
183 end
175 end # transaction
184 end # transaction
176 @current_revision_number = nil
185 @current_revision_number = nil
177 end
186 end
178
187
179 private
188 private
180
189
181 # Returns the next revision number to assign to a CVS changeset
190 # Returns the next revision number to assign to a CVS changeset
182 def next_revision_number
191 def next_revision_number
183 # Need to retrieve existing revision numbers to sort them as integers
192 # Need to retrieve existing revision numbers to sort them as integers
184 sql = "SELECT revision FROM #{Changeset.table_name} "
193 sql = "SELECT revision FROM #{Changeset.table_name} "
185 sql << "WHERE repository_id = #{id} AND revision NOT LIKE 'tmp%'"
194 sql << "WHERE repository_id = #{id} AND revision NOT LIKE 'tmp%'"
186 @current_revision_number ||= (connection.select_values(sql).collect(&:to_i).max || 0)
195 @current_revision_number ||= (connection.select_values(sql).collect(&:to_i).max || 0)
187 @current_revision_number += 1
196 @current_revision_number += 1
188 end
197 end
189 end
198 end
General Comments 0
You need to be logged in to leave comments. Login now