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