##// END OF EJS Templates
scm: cvs: convert author encoding with log encoding setting....
Toshi MARUYAMA -
r5336:04a22fa757a5
parent child
Show More
@@ -1,204 +1,204
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/cvs_adapter'
19 19 require 'digest/sha1'
20 20
21 21 class Repository::Cvs < Repository
22 22 validates_presence_of :url, :root_url, :log_encoding
23 23
24 24 ATTRIBUTE_KEY_NAMES = {
25 25 "url" => "CVSROOT",
26 26 "root_url" => "Module",
27 27 "log_encoding" => "Commit messages encoding",
28 28 }
29 29 def self.human_attribute_name(attribute_key_name)
30 30 ATTRIBUTE_KEY_NAMES[attribute_key_name] || super
31 31 end
32 32
33 33 def self.scm_adapter_class
34 34 Redmine::Scm::Adapters::CvsAdapter
35 35 end
36 36
37 37 def self.scm_name
38 38 'CVS'
39 39 end
40 40
41 41 def entry(path=nil, identifier=nil)
42 42 rev = identifier.nil? ? nil : changesets.find_by_revision(identifier)
43 43 scm.entry(path, rev.nil? ? nil : rev.committed_on)
44 44 end
45 45
46 46 def entries(path=nil, identifier=nil)
47 47 rev = nil
48 48 if ! identifier.nil?
49 49 rev = changesets.find_by_revision(identifier)
50 50 return nil if rev.nil?
51 51 end
52 52 entries = scm.entries(path, rev.nil? ? nil : rev.committed_on)
53 53 if entries
54 54 entries.each() do |entry|
55 55 if ( ! entry.lastrev.nil? ) && ( ! entry.lastrev.revision.nil? )
56 56 change=changes.find_by_revision_and_path(
57 57 entry.lastrev.revision,
58 58 scm.with_leading_slash(entry.path) )
59 59 if change
60 60 entry.lastrev.identifier = change.changeset.revision
61 61 entry.lastrev.revision = change.changeset.revision
62 62 entry.lastrev.author = change.changeset.committer
63 63 # entry.lastrev.branch = change.branch
64 64 end
65 65 end
66 66 end
67 67 end
68 68 entries
69 69 end
70 70
71 71 def cat(path, identifier=nil)
72 72 rev = nil
73 73 if ! identifier.nil?
74 74 rev = changesets.find_by_revision(identifier)
75 75 return nil if rev.nil?
76 76 end
77 77 scm.cat(path, rev.nil? ? nil : rev.committed_on)
78 78 end
79 79
80 80 def annotate(path, identifier=nil)
81 81 rev = nil
82 82 if ! identifier.nil?
83 83 rev = changesets.find_by_revision(identifier)
84 84 return nil if rev.nil?
85 85 end
86 86 scm.annotate(path, rev.nil? ? nil : rev.committed_on)
87 87 end
88 88
89 89 def diff(path, rev, rev_to)
90 90 # convert rev to revision. CVS can't handle changesets here
91 91 diff=[]
92 92 changeset_from = changesets.find_by_revision(rev)
93 93 if rev_to.to_i > 0
94 94 changeset_to = changesets.find_by_revision(rev_to)
95 95 end
96 96 changeset_from.changes.each() do |change_from|
97 97 revision_from = nil
98 98 revision_to = nil
99 99 if path.nil? || (change_from.path.starts_with? scm.with_leading_slash(path))
100 100 revision_from = change_from.revision
101 101 end
102 102 if revision_from
103 103 if changeset_to
104 104 changeset_to.changes.each() do |change_to|
105 105 revision_to=change_to.revision if change_to.path==change_from.path
106 106 end
107 107 end
108 108 unless revision_to
109 109 revision_to=scm.get_previous_revision(revision_from)
110 110 end
111 111 file_diff = scm.diff(change_from.path, revision_from, revision_to)
112 112 diff = diff + file_diff unless file_diff.nil?
113 113 end
114 114 end
115 115 return diff
116 116 end
117 117
118 118 def fetch_changesets
119 119 # some nifty bits to introduce a commit-id with cvs
120 120 # natively cvs doesn't provide any kind of changesets,
121 121 # there is only a revision per file.
122 122 # we now take a guess using the author, the commitlog and the commit-date.
123 123
124 124 # last one is the next step to take. the commit-date is not equal for all
125 125 # commits in one changeset. cvs update the commit-date when the *,v file was touched. so
126 126 # we use a small delta here, to merge all changes belonging to _one_ changeset
127 127 time_delta = 10.seconds
128 128 fetch_since = latest_changeset ? latest_changeset.committed_on : nil
129 129 transaction do
130 130 tmp_rev_num = 1
131 scm.revisions('', fetch_since, nil, :with_paths => true) do |revision|
131 scm.revisions('', fetch_since, nil, :log_encoding => repo_log_encoding) do |revision|
132 132 # only add the change to the database, if it doen't exists. the cvs log
133 133 # is not exclusive at all.
134 134 tmp_time = revision.time.clone
135 135 unless changes.find_by_path_and_revision(
136 136 scm.with_leading_slash(revision.paths[0][:path]),
137 137 revision.paths[0][:revision]
138 138 )
139 139 cmt = Changeset.normalize_comments(revision.message, repo_log_encoding)
140 140 author_utf8 = Changeset.to_utf8(revision.author, repo_log_encoding)
141 141 cs = changesets.find(
142 142 :first,
143 143 :conditions => {
144 144 :committed_on => tmp_time - time_delta .. tmp_time + time_delta,
145 145 :committer => author_utf8,
146 146 :comments => cmt
147 147 }
148 148 )
149 149 # create a new changeset....
150 150 unless cs
151 151 # we use a temporaray revision number here (just for inserting)
152 152 # later on, we calculate a continous positive number
153 153 tmp_time2 = tmp_time.clone.gmtime
154 154 branch = revision.paths[0][:branch]
155 155 scmid = branch + "-" + tmp_time2.strftime("%Y%m%d-%H%M%S")
156 156 cs = Changeset.create(:repository => self,
157 157 :revision => "tmp#{tmp_rev_num}",
158 158 :scmid => scmid,
159 159 :committer => revision.author,
160 160 :committed_on => tmp_time,
161 161 :comments => revision.message)
162 162 tmp_rev_num += 1
163 163 end
164 164 # convert CVS-File-States to internal Action-abbrevations
165 165 # default action is (M)odified
166 166 action = "M"
167 167 if revision.paths[0][:action] == "Exp" && revision.paths[0][:revision] == "1.1"
168 168 action = "A" # add-action always at first revision (= 1.1)
169 169 elsif revision.paths[0][:action] == "dead"
170 170 action = "D" # dead-state is similar to Delete
171 171 end
172 172 Change.create(
173 173 :changeset => cs,
174 174 :action => action,
175 175 :path => scm.with_leading_slash(revision.paths[0][:path]),
176 176 :revision => revision.paths[0][:revision],
177 177 :branch => revision.paths[0][:branch]
178 178 )
179 179 end
180 180 end
181 181
182 182 # Renumber new changesets in chronological order
183 183 changesets.find(
184 184 :all,
185 185 :order => 'committed_on ASC, id ASC',
186 186 :conditions => "revision LIKE 'tmp%'"
187 187 ).each do |changeset|
188 188 changeset.update_attribute :revision, next_revision_number
189 189 end
190 190 end # transaction
191 191 @current_revision_number = nil
192 192 end
193 193
194 194 private
195 195
196 196 # Returns the next revision number to assign to a CVS changeset
197 197 def next_revision_number
198 198 # Need to retrieve existing revision numbers to sort them as integers
199 199 sql = "SELECT revision FROM #{Changeset.table_name} "
200 200 sql << "WHERE repository_id = #{id} AND revision NOT LIKE 'tmp%'"
201 201 @current_revision_number ||= (connection.select_values(sql).collect(&:to_i).max || 0)
202 202 @current_revision_number += 1
203 203 end
204 204 end
@@ -1,448 +1,449
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/abstract_adapter'
19 19
20 20 module Redmine
21 21 module Scm
22 22 module Adapters
23 23 class CvsAdapter < AbstractAdapter
24 24
25 25 # CVS executable name
26 26 CVS_BIN = Redmine::Configuration['scm_cvs_command'] || "cvs"
27 27
28 28 # raised if scm command exited with error, e.g. unknown revision.
29 29 class ScmCommandAborted < CommandFailed; end
30 30
31 31 class << self
32 32 def client_command
33 33 @@bin ||= CVS_BIN
34 34 end
35 35
36 36 def sq_bin
37 37 @@sq_bin ||= shell_quote(CVS_BIN)
38 38 end
39 39
40 40 def client_version
41 41 @@client_version ||= (scm_command_version || [])
42 42 end
43 43
44 44 def client_available
45 45 client_version_above?([1, 12])
46 46 end
47 47
48 48 def scm_command_version
49 49 scm_version = scm_version_from_command_line.dup
50 50 if scm_version.respond_to?(:force_encoding)
51 51 scm_version.force_encoding('ASCII-8BIT')
52 52 end
53 53 if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)}m)
54 54 m[2].scan(%r{\d+}).collect(&:to_i)
55 55 end
56 56 end
57 57
58 58 def scm_version_from_command_line
59 59 shellout("#{sq_bin} --version") { |io| io.read }.to_s
60 60 end
61 61 end
62 62
63 63 # Guidelines for the input:
64 64 # url -> the project-path, relative to the cvsroot (eg. module name)
65 65 # root_url -> the good old, sometimes damned, CVSROOT
66 66 # login -> unnecessary
67 67 # password -> unnecessary too
68 68 def initialize(url, root_url=nil, login=nil, password=nil,
69 69 path_encoding=nil)
70 70 @url = url
71 71 # TODO: better Exception here (IllegalArgumentException)
72 72 raise CommandFailed if root_url.blank?
73 73 @root_url = root_url
74 74
75 75 # These are unused.
76 76 @login = login if login && !login.empty?
77 77 @password = (password || "") if @login
78 78 end
79 79
80 80 def info
81 81 logger.debug "<cvs> info"
82 82 Info.new({:root_url => @root_url, :lastrev => nil})
83 83 end
84 84
85 85 def get_previous_revision(revision)
86 86 CvsRevisionHelper.new(revision).prevRev
87 87 end
88 88
89 89 # Returns an Entries collection
90 90 # or nil if the given path doesn't exist in the repository
91 91 # this method is used by the repository-browser (aka LIST)
92 92 def entries(path=nil, identifier=nil)
93 93 logger.debug "<cvs> entries '#{path}' with identifier '#{identifier}'"
94 94 entries = Entries.new
95 95 cmd_args = %w|-q rls -e|
96 96 cmd_args << "-D" << time_to_cvstime_rlog(identifier) if identifier
97 97 cmd_args << path_with_proj(path)
98 98 scm_cmd(*cmd_args) do |io|
99 99 io.each_line() do |line|
100 100 fields = line.chop.split('/',-1)
101 101 logger.debug(">>InspectLine #{fields.inspect}")
102 102 if fields[0]!="D"
103 103 time = nil
104 104 # Thu Dec 13 16:27:22 2007
105 105 time_l = fields[-3].split(' ')
106 106 if time_l.size == 5 && time_l[4].length == 4
107 107 begin
108 108 time = Time.parse(
109 109 "#{time_l[1]} #{time_l[2]} #{time_l[3]} GMT #{time_l[4]}")
110 110 rescue
111 111 end
112 112 end
113 113 entries << Entry.new(
114 114 {
115 115 :name => fields[-5],
116 116 #:path => fields[-4].include?(path)?fields[-4]:(path + "/"+ fields[-4]),
117 117 :path => "#{path}/#{fields[-5]}",
118 118 :kind => 'file',
119 119 :size => nil,
120 120 :lastrev => Revision.new(
121 121 {
122 122 :revision => fields[-4],
123 123 :name => fields[-4],
124 124 :time => time,
125 125 :author => ''
126 126 })
127 127 })
128 128 else
129 129 entries << Entry.new(
130 130 {
131 131 :name => fields[1],
132 132 :path => "#{path}/#{fields[1]}",
133 133 :kind => 'dir',
134 134 :size => nil,
135 135 :lastrev => nil
136 136 })
137 137 end
138 138 end
139 139 end
140 140 entries.sort_by_name
141 141 rescue ScmCommandAborted
142 142 nil
143 143 end
144 144
145 145 STARTLOG="----------------------------"
146 146 ENDLOG ="============================================================================="
147 147
148 148 # Returns all revisions found between identifier_from and identifier_to
149 149 # in the repository. both identifier have to be dates or nil.
150 150 # these method returns nothing but yield every result in block
151 151 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}, &block)
152 152 logger.debug "<cvs> revisions path:" +
153 153 "'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
154 154 cmd_args = %w|-q rlog|
155 155 cmd_args << "-d" << ">#{time_to_cvstime_rlog(identifier_from)}" if identifier_from
156 156 cmd_args << path_with_proj(path)
157 157 scm_cmd(*cmd_args) do |io|
158 158 state = "entry_start"
159 159 commit_log = String.new
160 160 revision = nil
161 161 date = nil
162 162 author = nil
163 163 entry_path = nil
164 164 entry_name = nil
165 165 file_state = nil
166 166 branch_map = nil
167 167 io.each_line() do |line|
168 168 if state != "revision" && /^#{ENDLOG}/ =~ line
169 169 commit_log = String.new
170 170 revision = nil
171 171 state = "entry_start"
172 172 end
173 173 if state == "entry_start"
174 174 branch_map = Hash.new
175 175 if /^RCS file: #{Regexp.escape(root_url_path)}\/#{Regexp.escape(path_with_proj(path))}(.+),v$/ =~ line
176 176 entry_path = normalize_cvs_path($1)
177 177 entry_name = normalize_path(File.basename($1))
178 178 logger.debug("Path #{entry_path} <=> Name #{entry_name}")
179 179 elsif /^head: (.+)$/ =~ line
180 180 entry_headRev = $1 #unless entry.nil?
181 181 elsif /^symbolic names:/ =~ line
182 182 state = "symbolic" #unless entry.nil?
183 183 elsif /^#{STARTLOG}/ =~ line
184 184 commit_log = String.new
185 185 state = "revision"
186 186 end
187 187 next
188 188 elsif state == "symbolic"
189 189 if /^(.*):\s(.*)/ =~ (line.strip)
190 190 branch_map[$1] = $2
191 191 else
192 192 state = "tags"
193 193 next
194 194 end
195 195 elsif state == "tags"
196 196 if /^#{STARTLOG}/ =~ line
197 197 commit_log = ""
198 198 state = "revision"
199 199 elsif /^#{ENDLOG}/ =~ line
200 200 state = "head"
201 201 end
202 202 next
203 203 elsif state == "revision"
204 204 if /^#{ENDLOG}/ =~ line || /^#{STARTLOG}/ =~ line
205 205 if revision
206 206 revHelper = CvsRevisionHelper.new(revision)
207 207 revBranch = "HEAD"
208 208 branch_map.each() do |branch_name, branch_point|
209 209 if revHelper.is_in_branch_with_symbol(branch_point)
210 210 revBranch = branch_name
211 211 end
212 212 end
213 213 logger.debug("********** YIELD Revision #{revision}::#{revBranch}")
214 214 yield Revision.new({
215 215 :time => date,
216 216 :author => author,
217 217 :message => commit_log.chomp,
218 218 :paths => [{
219 219 :revision => revision,
220 220 :branch => revBranch,
221 221 :path => entry_path,
222 222 :name => entry_name,
223 223 :kind => 'file',
224 224 :action => file_state
225 225 }]
226 226 })
227 227 end
228 228 commit_log = String.new
229 229 revision = nil
230 230 if /^#{ENDLOG}/ =~ line
231 231 state = "entry_start"
232 232 end
233 233 next
234 234 end
235 235
236 236 if /^branches: (.+)$/ =~ line
237 237 # TODO: version.branch = $1
238 238 elsif /^revision (\d+(?:\.\d+)+).*$/ =~ line
239 239 revision = $1
240 240 elsif /^date:\s+(\d+.\d+.\d+\s+\d+:\d+:\d+)/ =~ line
241 241 date = Time.parse($1)
242 # TODO: This regexp fails in some non UTF-8 chars on Ruby 1.8.
243 author = /author: ([^;]+)/.match(line)[1]
244 file_state = /state: ([^;]+)/.match(line)[1]
242 line_utf8 = scm_iconv('UTF-8', options[:log_encoding], line)
243 author_utf8 = /author: ([^;]+)/.match(line_utf8)[1]
244 author = scm_iconv(options[:log_encoding], 'UTF-8', author_utf8)
245 file_state = /state: ([^;]+)/.match(line)[1]
245 246 # TODO:
246 247 # linechanges only available in CVS....
247 248 # maybe a feature our SVN implementation.
248 249 # I'm sure, they are useful for stats or something else
249 250 # linechanges =/lines: \+(\d+) -(\d+)/.match(line)
250 251 # unless linechanges.nil?
251 252 # version.line_plus = linechanges[1]
252 253 # version.line_minus = linechanges[2]
253 254 # else
254 255 # version.line_plus = 0
255 256 # version.line_minus = 0
256 257 # end
257 258 else
258 259 commit_log << line unless line =~ /^\*\*\* empty log message \*\*\*/
259 260 end
260 261 end
261 262 end
262 263 end
263 264 rescue ScmCommandAborted
264 265 Revisions.new
265 266 end
266 267
267 268 def diff(path, identifier_from, identifier_to=nil)
268 269 logger.debug "<cvs> diff path:'#{path}'" +
269 270 ",identifier_from #{identifier_from}, identifier_to #{identifier_to}"
270 271 cmd_args = %w|rdiff -u|
271 272 cmd_args << "-r#{identifier_to}"
272 273 cmd_args << "-r#{identifier_from}"
273 274 cmd_args << path_with_proj(path)
274 275 diff = []
275 276 scm_cmd(*cmd_args) do |io|
276 277 io.each_line do |line|
277 278 diff << line
278 279 end
279 280 end
280 281 diff
281 282 rescue ScmCommandAborted
282 283 nil
283 284 end
284 285
285 286 def cat(path, identifier=nil)
286 287 identifier = (identifier) ? identifier : "HEAD"
287 288 logger.debug "<cvs> cat path:'#{path}',identifier #{identifier}"
288 289 cmd_args = %w|-q co|
289 290 cmd_args << "-D" << time_to_cvstime(identifier) if identifier
290 291 cmd_args << "-p" << path_with_proj(path)
291 292 cat = nil
292 293 scm_cmd(*cmd_args) do |io|
293 294 io.binmode
294 295 cat = io.read
295 296 end
296 297 cat
297 298 rescue ScmCommandAborted
298 299 nil
299 300 end
300 301
301 302 def annotate(path, identifier=nil)
302 303 identifier = (identifier) ? identifier : "HEAD"
303 304 logger.debug "<cvs> annotate path:'#{path}',identifier #{identifier}"
304 305 cmd_args = %w|rannotate|
305 306 cmd_args << "-D" << time_to_cvstime(identifier) if identifier
306 307 cmd_args << path_with_proj(path)
307 308 blame = Annotate.new
308 309 scm_cmd(*cmd_args) do |io|
309 310 io.each_line do |line|
310 311 next unless line =~ %r{^([\d\.]+)\s+\(([^\)]+)\s+[^\)]+\):\s(.*)$}
311 312 blame.add_line(
312 313 $3.rstrip,
313 314 Revision.new(
314 315 :revision => $1,
315 316 :identifier => nil,
316 317 :author => $2.strip
317 318 ))
318 319 end
319 320 end
320 321 blame
321 322 rescue ScmCommandAborted
322 323 Annotate.new
323 324 end
324 325
325 326 private
326 327
327 328 # Returns the root url without the connexion string
328 329 # :pserver:anonymous@foo.bar:/path => /path
329 330 # :ext:cvsservername:/path => /path
330 331 def root_url_path
331 332 root_url.to_s.gsub(/^:.+:\d*/, '')
332 333 end
333 334
334 335 # convert a date/time into the CVS-format
335 336 def time_to_cvstime(time)
336 337 return nil if time.nil?
337 338 return Time.now if time == 'HEAD'
338 339
339 340 unless time.kind_of? Time
340 341 time = Time.parse(time)
341 342 end
342 343 return time.strftime("%Y-%m-%d %H:%M:%S")
343 344 end
344 345
345 346 def time_to_cvstime_rlog(time)
346 347 return nil if time.nil?
347 348 t1 = time.clone.localtime
348 349 return t1.strftime("%Y-%m-%d %H:%M:%S")
349 350 end
350 351
351 352 def normalize_cvs_path(path)
352 353 normalize_path(path.gsub(/Attic\//,''))
353 354 end
354 355
355 356 def normalize_path(path)
356 357 path.sub(/^(\/)*(.*)/,'\2').sub(/(.*)(,v)+/,'\1')
357 358 end
358 359
359 360 def path_with_proj(path)
360 361 "#{url}#{with_leading_slash(path)}"
361 362 end
362 363 private :path_with_proj
363 364
364 365 class Revision < Redmine::Scm::Adapters::Revision
365 366 # Returns the readable identifier
366 367 def format_identifier
367 368 revision.to_s
368 369 end
369 370 end
370 371
371 372 def scm_cmd(*args, &block)
372 373 full_args = [CVS_BIN, '-d', root_url]
373 374 full_args += args
374 375 ret = shellout(full_args.map { |e| shell_quote e.to_s }.join(' '), &block)
375 376 if $? && $?.exitstatus != 0
376 377 raise ScmCommandAborted, "cvs exited with non-zero status: #{$?.exitstatus}"
377 378 end
378 379 ret
379 380 end
380 381 private :scm_cmd
381 382 end
382 383
383 384 class CvsRevisionHelper
384 385 attr_accessor :complete_rev, :revision, :base, :branchid
385 386
386 387 def initialize(complete_rev)
387 388 @complete_rev = complete_rev
388 389 parseRevision()
389 390 end
390 391
391 392 def branchPoint
392 393 return @base
393 394 end
394 395
395 396 def branchVersion
396 397 if isBranchRevision
397 398 return @base+"."+@branchid
398 399 end
399 400 return @base
400 401 end
401 402
402 403 def isBranchRevision
403 404 !@branchid.nil?
404 405 end
405 406
406 407 def prevRev
407 408 unless @revision == 0
408 409 return buildRevision( @revision - 1 )
409 410 end
410 411 return buildRevision( @revision )
411 412 end
412 413
413 414 def is_in_branch_with_symbol(branch_symbol)
414 415 bpieces = branch_symbol.split(".")
415 416 branch_start = "#{bpieces[0..-3].join(".")}.#{bpieces[-1]}"
416 417 return ( branchVersion == branch_start )
417 418 end
418 419
419 420 private
420 421 def buildRevision(rev)
421 422 if rev == 0
422 423 if @branchid.nil?
423 424 @base + ".0"
424 425 else
425 426 @base
426 427 end
427 428 elsif @branchid.nil?
428 429 @base + "." + rev.to_s
429 430 else
430 431 @base + "." + @branchid + "." + rev.to_s
431 432 end
432 433 end
433 434
434 435 # Interpretiert die cvs revisionsnummern wie z.b. 1.14 oder 1.3.0.15
435 436 def parseRevision()
436 437 pieces = @complete_rev.split(".")
437 438 @revision = pieces.last.to_i
438 439 baseSize = 1
439 440 baseSize += (pieces.size / 2)
440 441 @base = pieces[0..-baseSize].join(".")
441 442 if baseSize > 2
442 443 @branchid = pieces[-2]
443 444 end
444 445 end
445 446 end
446 447 end
447 448 end
448 449 end
@@ -1,67 +1,67
1 1 require File.expand_path('../../../../../../test_helper', __FILE__)
2 2 begin
3 3 require 'mocha'
4 4
5 5 class CvsAdapterTest < ActiveSupport::TestCase
6 6
7 7 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/cvs_repository'
8 8 REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin?
9 9 MODULE_NAME = 'test'
10 10
11 11 if File.directory?(REPOSITORY_PATH)
12 12 def setup
13 13 @adapter = Redmine::Scm::Adapters::CvsAdapter.new(MODULE_NAME, REPOSITORY_PATH)
14 14 end
15 15
16 16 def test_scm_version
17 17 to_test = { "\nConcurrent Versions System (CVS) 1.12.13 (client/server)\n" => [1,12,13],
18 18 "\r\n1.12.12\r\n1.12.11" => [1,12,12],
19 19 "1.12.11\r\n1.12.10\r\n" => [1,12,11]}
20 20 to_test.each do |s, v|
21 21 test_scm_version_for(s, v)
22 22 end
23 23 end
24 24
25 25 def test_revisions_all
26 26 cnt = 0
27 @adapter.revisions('', nil, nil, :with_paths => true) do |revision|
27 @adapter.revisions('', nil, nil, :log_encoding => 'UTF-8') do |revision|
28 28 cnt += 1
29 29 end
30 30 assert_equal 16, cnt
31 31 end
32 32
33 33 def test_revisions_from_rev3
34 34 rev3_committed_on = Time.gm(2007, 12, 13, 16, 27, 22)
35 35 cnt = 0
36 @adapter.revisions('', rev3_committed_on, nil, :with_paths => true) do |revision|
36 @adapter.revisions('', rev3_committed_on, nil, :log_encoding => 'UTF-8') do |revision|
37 37 cnt += 1
38 38 end
39 39 assert_equal 4, cnt
40 40 end
41 41
42 42 def test_entries_rev3
43 43 rev3_committed_on = Time.gm(2007, 12, 13, 16, 27, 22)
44 44 entries = @adapter.entries('sources', rev3_committed_on)
45 45 assert_equal 2, entries.size
46 46 assert_equal entries[0].name, "watchers_controller.rb"
47 47 assert_equal entries[0].lastrev.time, Time.gm(2007, 12, 13, 16, 27, 22)
48 48 end
49 49
50 50 private
51 51
52 52 def test_scm_version_for(scm_command_version, version)
53 53 @adapter.class.expects(:scm_version_from_command_line).returns(scm_command_version)
54 54 assert_equal version, @adapter.class.scm_command_version
55 55 end
56 56 else
57 57 puts "Cvs test repository NOT FOUND. Skipping unit tests !!!"
58 58 def test_fake; assert true end
59 59 end
60 60 end
61 61
62 62 rescue LoadError
63 63 class CvsMochaFake < ActiveSupport::TestCase
64 64 def test_fake; assert(false, "Requires mocha to run those tests") end
65 65 end
66 66 end
67 67
General Comments 0
You need to be logged in to leave comments. Login now