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