##// END OF EJS Templates
scm: cvs: add comment of author regexp fails in some non UTF-8 chars on Ruby 1.8....
Toshi MARUYAMA -
r5334:7e5bc046d68d
parent child
Show More
@@ -1,447 +1,448
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 # TODO: better Exception here (IllegalArgumentException)
71 # TODO: better Exception here (IllegalArgumentException)
72 raise CommandFailed if root_url.blank?
72 raise CommandFailed if root_url.blank?
73 @root_url = root_url
73 @root_url = root_url
74
74
75 # These are unused.
75 # These are unused.
76 @login = login if login && !login.empty?
76 @login = login if login && !login.empty?
77 @password = (password || "") if @login
77 @password = (password || "") if @login
78 end
78 end
79
79
80 def info
80 def info
81 logger.debug "<cvs> info"
81 logger.debug "<cvs> info"
82 Info.new({:root_url => @root_url, :lastrev => nil})
82 Info.new({:root_url => @root_url, :lastrev => nil})
83 end
83 end
84
84
85 def get_previous_revision(revision)
85 def get_previous_revision(revision)
86 CvsRevisionHelper.new(revision).prevRev
86 CvsRevisionHelper.new(revision).prevRev
87 end
87 end
88
88
89 # Returns an Entries collection
89 # Returns an Entries collection
90 # or nil if the given path doesn't exist in the repository
90 # or nil if the given path doesn't exist in the repository
91 # this method is used by the repository-browser (aka LIST)
91 # this method is used by the repository-browser (aka LIST)
92 def entries(path=nil, identifier=nil)
92 def entries(path=nil, identifier=nil)
93 logger.debug "<cvs> entries '#{path}' with identifier '#{identifier}'"
93 logger.debug "<cvs> entries '#{path}' with identifier '#{identifier}'"
94 entries = Entries.new
94 entries = Entries.new
95 cmd_args = %w|-q rls -e|
95 cmd_args = %w|-q rls -e|
96 cmd_args << "-D" << time_to_cvstime_rlog(identifier) if identifier
96 cmd_args << "-D" << time_to_cvstime_rlog(identifier) if identifier
97 cmd_args << path_with_proj(path)
97 cmd_args << path_with_proj(path)
98 scm_cmd(*cmd_args) do |io|
98 scm_cmd(*cmd_args) do |io|
99 io.each_line() do |line|
99 io.each_line() do |line|
100 fields = line.chop.split('/',-1)
100 fields = line.chop.split('/',-1)
101 logger.debug(">>InspectLine #{fields.inspect}")
101 logger.debug(">>InspectLine #{fields.inspect}")
102 if fields[0]!="D"
102 if fields[0]!="D"
103 time = nil
103 time = nil
104 # Thu Dec 13 16:27:22 2007
104 # Thu Dec 13 16:27:22 2007
105 time_l = fields[-3].split(' ')
105 time_l = fields[-3].split(' ')
106 if time_l.size == 5 && time_l[4].length == 4
106 if time_l.size == 5 && time_l[4].length == 4
107 begin
107 begin
108 time = Time.parse(
108 time = Time.parse(
109 "#{time_l[1]} #{time_l[2]} #{time_l[3]} GMT #{time_l[4]}")
109 "#{time_l[1]} #{time_l[2]} #{time_l[3]} GMT #{time_l[4]}")
110 rescue
110 rescue
111 end
111 end
112 end
112 end
113 entries << Entry.new(
113 entries << Entry.new(
114 {
114 {
115 :name => fields[-5],
115 :name => fields[-5],
116 #:path => fields[-4].include?(path)?fields[-4]:(path + "/"+ fields[-4]),
116 #:path => fields[-4].include?(path)?fields[-4]:(path + "/"+ fields[-4]),
117 :path => "#{path}/#{fields[-5]}",
117 :path => "#{path}/#{fields[-5]}",
118 :kind => 'file',
118 :kind => 'file',
119 :size => nil,
119 :size => nil,
120 :lastrev => Revision.new(
120 :lastrev => Revision.new(
121 {
121 {
122 :revision => fields[-4],
122 :revision => fields[-4],
123 :name => fields[-4],
123 :name => fields[-4],
124 :time => time,
124 :time => time,
125 :author => ''
125 :author => ''
126 })
126 })
127 })
127 })
128 else
128 else
129 entries << Entry.new(
129 entries << Entry.new(
130 {
130 {
131 :name => fields[1],
131 :name => fields[1],
132 :path => "#{path}/#{fields[1]}",
132 :path => "#{path}/#{fields[1]}",
133 :kind => 'dir',
133 :kind => 'dir',
134 :size => nil,
134 :size => nil,
135 :lastrev => nil
135 :lastrev => nil
136 })
136 })
137 end
137 end
138 end
138 end
139 end
139 end
140 entries.sort_by_name
140 entries.sort_by_name
141 rescue ScmCommandAborted
141 rescue ScmCommandAborted
142 nil
142 nil
143 end
143 end
144
144
145 STARTLOG="----------------------------"
145 STARTLOG="----------------------------"
146 ENDLOG ="============================================================================="
146 ENDLOG ="============================================================================="
147
147
148 # Returns all revisions found between identifier_from and identifier_to
148 # Returns all revisions found between identifier_from and identifier_to
149 # in the repository. both identifier have to be dates or nil.
149 # in the repository. both identifier have to be dates or nil.
150 # these method returns nothing but yield every result in block
150 # these method returns nothing but yield every result in block
151 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}, &block)
151 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}, &block)
152 logger.debug "<cvs> revisions path:" +
152 logger.debug "<cvs> revisions path:" +
153 "'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
153 "'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
154 cmd_args = %w|-q rlog|
154 cmd_args = %w|-q rlog|
155 cmd_args << "-d" << ">#{time_to_cvstime_rlog(identifier_from)}" if identifier_from
155 cmd_args << "-d" << ">#{time_to_cvstime_rlog(identifier_from)}" if identifier_from
156 cmd_args << path_with_proj(path)
156 cmd_args << path_with_proj(path)
157 scm_cmd(*cmd_args) do |io|
157 scm_cmd(*cmd_args) do |io|
158 state = "entry_start"
158 state = "entry_start"
159 commit_log = String.new
159 commit_log = String.new
160 revision = nil
160 revision = nil
161 date = nil
161 date = nil
162 author = nil
162 author = nil
163 entry_path = nil
163 entry_path = nil
164 entry_name = nil
164 entry_name = nil
165 file_state = nil
165 file_state = nil
166 branch_map = nil
166 branch_map = nil
167 io.each_line() do |line|
167 io.each_line() do |line|
168 if state != "revision" && /^#{ENDLOG}/ =~ line
168 if state != "revision" && /^#{ENDLOG}/ =~ line
169 commit_log = String.new
169 commit_log = String.new
170 revision = nil
170 revision = nil
171 state = "entry_start"
171 state = "entry_start"
172 end
172 end
173 if state == "entry_start"
173 if state == "entry_start"
174 branch_map = Hash.new
174 branch_map = Hash.new
175 if /^RCS file: #{Regexp.escape(root_url_path)}\/#{Regexp.escape(path_with_proj(path))}(.+),v$/ =~ line
175 if /^RCS file: #{Regexp.escape(root_url_path)}\/#{Regexp.escape(path_with_proj(path))}(.+),v$/ =~ line
176 entry_path = normalize_cvs_path($1)
176 entry_path = normalize_cvs_path($1)
177 entry_name = normalize_path(File.basename($1))
177 entry_name = normalize_path(File.basename($1))
178 logger.debug("Path #{entry_path} <=> Name #{entry_name}")
178 logger.debug("Path #{entry_path} <=> Name #{entry_name}")
179 elsif /^head: (.+)$/ =~ line
179 elsif /^head: (.+)$/ =~ line
180 entry_headRev = $1 #unless entry.nil?
180 entry_headRev = $1 #unless entry.nil?
181 elsif /^symbolic names:/ =~ line
181 elsif /^symbolic names:/ =~ line
182 state = "symbolic" #unless entry.nil?
182 state = "symbolic" #unless entry.nil?
183 elsif /^#{STARTLOG}/ =~ line
183 elsif /^#{STARTLOG}/ =~ line
184 commit_log = String.new
184 commit_log = String.new
185 state = "revision"
185 state = "revision"
186 end
186 end
187 next
187 next
188 elsif state == "symbolic"
188 elsif state == "symbolic"
189 if /^(.*):\s(.*)/ =~ (line.strip)
189 if /^(.*):\s(.*)/ =~ (line.strip)
190 branch_map[$1] = $2
190 branch_map[$1] = $2
191 else
191 else
192 state = "tags"
192 state = "tags"
193 next
193 next
194 end
194 end
195 elsif state == "tags"
195 elsif state == "tags"
196 if /^#{STARTLOG}/ =~ line
196 if /^#{STARTLOG}/ =~ line
197 commit_log = ""
197 commit_log = ""
198 state = "revision"
198 state = "revision"
199 elsif /^#{ENDLOG}/ =~ line
199 elsif /^#{ENDLOG}/ =~ line
200 state = "head"
200 state = "head"
201 end
201 end
202 next
202 next
203 elsif state == "revision"
203 elsif state == "revision"
204 if /^#{ENDLOG}/ =~ line || /^#{STARTLOG}/ =~ line
204 if /^#{ENDLOG}/ =~ line || /^#{STARTLOG}/ =~ line
205 if revision
205 if revision
206 revHelper = CvsRevisionHelper.new(revision)
206 revHelper = CvsRevisionHelper.new(revision)
207 revBranch = "HEAD"
207 revBranch = "HEAD"
208 branch_map.each() do |branch_name, branch_point|
208 branch_map.each() do |branch_name, branch_point|
209 if revHelper.is_in_branch_with_symbol(branch_point)
209 if revHelper.is_in_branch_with_symbol(branch_point)
210 revBranch = branch_name
210 revBranch = branch_name
211 end
211 end
212 end
212 end
213 logger.debug("********** YIELD Revision #{revision}::#{revBranch}")
213 logger.debug("********** YIELD Revision #{revision}::#{revBranch}")
214 yield Revision.new({
214 yield Revision.new({
215 :time => date,
215 :time => date,
216 :author => author,
216 :author => author,
217 :message => commit_log.chomp,
217 :message => commit_log.chomp,
218 :paths => [{
218 :paths => [{
219 :revision => revision,
219 :revision => revision,
220 :branch => revBranch,
220 :branch => revBranch,
221 :path => entry_path,
221 :path => entry_path,
222 :name => entry_name,
222 :name => entry_name,
223 :kind => 'file',
223 :kind => 'file',
224 :action => file_state
224 :action => file_state
225 }]
225 }]
226 })
226 })
227 end
227 end
228 commit_log = String.new
228 commit_log = String.new
229 revision = nil
229 revision = nil
230 if /^#{ENDLOG}/ =~ line
230 if /^#{ENDLOG}/ =~ line
231 state = "entry_start"
231 state = "entry_start"
232 end
232 end
233 next
233 next
234 end
234 end
235
235
236 if /^branches: (.+)$/ =~ line
236 if /^branches: (.+)$/ =~ line
237 # TODO: version.branch = $1
237 # TODO: version.branch = $1
238 elsif /^revision (\d+(?:\.\d+)+).*$/ =~ line
238 elsif /^revision (\d+(?:\.\d+)+).*$/ =~ line
239 revision = $1
239 revision = $1
240 elsif /^date:\s+(\d+.\d+.\d+\s+\d+:\d+:\d+)/ =~ line
240 elsif /^date:\s+(\d+.\d+.\d+\s+\d+:\d+:\d+)/ =~ line
241 date = Time.parse($1)
241 date = Time.parse($1)
242 # TODO: This regexp fails in some non UTF-8 chars on Ruby 1.8.
242 author = /author: ([^;]+)/.match(line)[1]
243 author = /author: ([^;]+)/.match(line)[1]
243 file_state = /state: ([^;]+)/.match(line)[1]
244 file_state = /state: ([^;]+)/.match(line)[1]
244 # TODO:
245 # TODO:
245 # linechanges only available in CVS....
246 # linechanges only available in CVS....
246 # maybe a feature our SVN implementation.
247 # maybe a feature our SVN implementation.
247 # I'm sure, they are useful for stats or something else
248 # I'm sure, they are useful for stats or something else
248 # linechanges =/lines: \+(\d+) -(\d+)/.match(line)
249 # linechanges =/lines: \+(\d+) -(\d+)/.match(line)
249 # unless linechanges.nil?
250 # unless linechanges.nil?
250 # version.line_plus = linechanges[1]
251 # version.line_plus = linechanges[1]
251 # version.line_minus = linechanges[2]
252 # version.line_minus = linechanges[2]
252 # else
253 # else
253 # version.line_plus = 0
254 # version.line_plus = 0
254 # version.line_minus = 0
255 # version.line_minus = 0
255 # end
256 # end
256 else
257 else
257 commit_log << line unless line =~ /^\*\*\* empty log message \*\*\*/
258 commit_log << line unless line =~ /^\*\*\* empty log message \*\*\*/
258 end
259 end
259 end
260 end
260 end
261 end
261 end
262 end
262 rescue ScmCommandAborted
263 rescue ScmCommandAborted
263 Revisions.new
264 Revisions.new
264 end
265 end
265
266
266 def diff(path, identifier_from, identifier_to=nil)
267 def diff(path, identifier_from, identifier_to=nil)
267 logger.debug "<cvs> diff path:'#{path}'" +
268 logger.debug "<cvs> diff path:'#{path}'" +
268 ",identifier_from #{identifier_from}, identifier_to #{identifier_to}"
269 ",identifier_from #{identifier_from}, identifier_to #{identifier_to}"
269 cmd_args = %w|rdiff -u|
270 cmd_args = %w|rdiff -u|
270 cmd_args << "-r#{identifier_to}"
271 cmd_args << "-r#{identifier_to}"
271 cmd_args << "-r#{identifier_from}"
272 cmd_args << "-r#{identifier_from}"
272 cmd_args << path_with_proj(path)
273 cmd_args << path_with_proj(path)
273 diff = []
274 diff = []
274 scm_cmd(*cmd_args) do |io|
275 scm_cmd(*cmd_args) do |io|
275 io.each_line do |line|
276 io.each_line do |line|
276 diff << line
277 diff << line
277 end
278 end
278 end
279 end
279 diff
280 diff
280 rescue ScmCommandAborted
281 rescue ScmCommandAborted
281 nil
282 nil
282 end
283 end
283
284
284 def cat(path, identifier=nil)
285 def cat(path, identifier=nil)
285 identifier = (identifier) ? identifier : "HEAD"
286 identifier = (identifier) ? identifier : "HEAD"
286 logger.debug "<cvs> cat path:'#{path}',identifier #{identifier}"
287 logger.debug "<cvs> cat path:'#{path}',identifier #{identifier}"
287 cmd_args = %w|-q co|
288 cmd_args = %w|-q co|
288 cmd_args << "-D" << time_to_cvstime(identifier) if identifier
289 cmd_args << "-D" << time_to_cvstime(identifier) if identifier
289 cmd_args << "-p" << path_with_proj(path)
290 cmd_args << "-p" << path_with_proj(path)
290 cat = nil
291 cat = nil
291 scm_cmd(*cmd_args) do |io|
292 scm_cmd(*cmd_args) do |io|
292 io.binmode
293 io.binmode
293 cat = io.read
294 cat = io.read
294 end
295 end
295 cat
296 cat
296 rescue ScmCommandAborted
297 rescue ScmCommandAborted
297 nil
298 nil
298 end
299 end
299
300
300 def annotate(path, identifier=nil)
301 def annotate(path, identifier=nil)
301 identifier = (identifier) ? identifier : "HEAD"
302 identifier = (identifier) ? identifier : "HEAD"
302 logger.debug "<cvs> annotate path:'#{path}',identifier #{identifier}"
303 logger.debug "<cvs> annotate path:'#{path}',identifier #{identifier}"
303 cmd_args = %w|rannotate|
304 cmd_args = %w|rannotate|
304 cmd_args << "-D" << time_to_cvstime(identifier) if identifier
305 cmd_args << "-D" << time_to_cvstime(identifier) if identifier
305 cmd_args << path_with_proj(path)
306 cmd_args << path_with_proj(path)
306 blame = Annotate.new
307 blame = Annotate.new
307 scm_cmd(*cmd_args) do |io|
308 scm_cmd(*cmd_args) do |io|
308 io.each_line do |line|
309 io.each_line do |line|
309 next unless line =~ %r{^([\d\.]+)\s+\(([^\)]+)\s+[^\)]+\):\s(.*)$}
310 next unless line =~ %r{^([\d\.]+)\s+\(([^\)]+)\s+[^\)]+\):\s(.*)$}
310 blame.add_line(
311 blame.add_line(
311 $3.rstrip,
312 $3.rstrip,
312 Revision.new(
313 Revision.new(
313 :revision => $1,
314 :revision => $1,
314 :identifier => nil,
315 :identifier => nil,
315 :author => $2.strip
316 :author => $2.strip
316 ))
317 ))
317 end
318 end
318 end
319 end
319 blame
320 blame
320 rescue ScmCommandAborted
321 rescue ScmCommandAborted
321 Annotate.new
322 Annotate.new
322 end
323 end
323
324
324 private
325 private
325
326
326 # Returns the root url without the connexion string
327 # Returns the root url without the connexion string
327 # :pserver:anonymous@foo.bar:/path => /path
328 # :pserver:anonymous@foo.bar:/path => /path
328 # :ext:cvsservername:/path => /path
329 # :ext:cvsservername:/path => /path
329 def root_url_path
330 def root_url_path
330 root_url.to_s.gsub(/^:.+:\d*/, '')
331 root_url.to_s.gsub(/^:.+:\d*/, '')
331 end
332 end
332
333
333 # convert a date/time into the CVS-format
334 # convert a date/time into the CVS-format
334 def time_to_cvstime(time)
335 def time_to_cvstime(time)
335 return nil if time.nil?
336 return nil if time.nil?
336 return Time.now if time == 'HEAD'
337 return Time.now if time == 'HEAD'
337
338
338 unless time.kind_of? Time
339 unless time.kind_of? Time
339 time = Time.parse(time)
340 time = Time.parse(time)
340 end
341 end
341 return time.strftime("%Y-%m-%d %H:%M:%S")
342 return time.strftime("%Y-%m-%d %H:%M:%S")
342 end
343 end
343
344
344 def time_to_cvstime_rlog(time)
345 def time_to_cvstime_rlog(time)
345 return nil if time.nil?
346 return nil if time.nil?
346 t1 = time.clone.localtime
347 t1 = time.clone.localtime
347 return t1.strftime("%Y-%m-%d %H:%M:%S")
348 return t1.strftime("%Y-%m-%d %H:%M:%S")
348 end
349 end
349
350
350 def normalize_cvs_path(path)
351 def normalize_cvs_path(path)
351 normalize_path(path.gsub(/Attic\//,''))
352 normalize_path(path.gsub(/Attic\//,''))
352 end
353 end
353
354
354 def normalize_path(path)
355 def normalize_path(path)
355 path.sub(/^(\/)*(.*)/,'\2').sub(/(.*)(,v)+/,'\1')
356 path.sub(/^(\/)*(.*)/,'\2').sub(/(.*)(,v)+/,'\1')
356 end
357 end
357
358
358 def path_with_proj(path)
359 def path_with_proj(path)
359 "#{url}#{with_leading_slash(path)}"
360 "#{url}#{with_leading_slash(path)}"
360 end
361 end
361 private :path_with_proj
362 private :path_with_proj
362
363
363 class Revision < Redmine::Scm::Adapters::Revision
364 class Revision < Redmine::Scm::Adapters::Revision
364 # Returns the readable identifier
365 # Returns the readable identifier
365 def format_identifier
366 def format_identifier
366 revision.to_s
367 revision.to_s
367 end
368 end
368 end
369 end
369
370
370 def scm_cmd(*args, &block)
371 def scm_cmd(*args, &block)
371 full_args = [CVS_BIN, '-d', root_url]
372 full_args = [CVS_BIN, '-d', root_url]
372 full_args += args
373 full_args += args
373 ret = shellout(full_args.map { |e| shell_quote e.to_s }.join(' '), &block)
374 ret = shellout(full_args.map { |e| shell_quote e.to_s }.join(' '), &block)
374 if $? && $?.exitstatus != 0
375 if $? && $?.exitstatus != 0
375 raise ScmCommandAborted, "cvs exited with non-zero status: #{$?.exitstatus}"
376 raise ScmCommandAborted, "cvs exited with non-zero status: #{$?.exitstatus}"
376 end
377 end
377 ret
378 ret
378 end
379 end
379 private :scm_cmd
380 private :scm_cmd
380 end
381 end
381
382
382 class CvsRevisionHelper
383 class CvsRevisionHelper
383 attr_accessor :complete_rev, :revision, :base, :branchid
384 attr_accessor :complete_rev, :revision, :base, :branchid
384
385
385 def initialize(complete_rev)
386 def initialize(complete_rev)
386 @complete_rev = complete_rev
387 @complete_rev = complete_rev
387 parseRevision()
388 parseRevision()
388 end
389 end
389
390
390 def branchPoint
391 def branchPoint
391 return @base
392 return @base
392 end
393 end
393
394
394 def branchVersion
395 def branchVersion
395 if isBranchRevision
396 if isBranchRevision
396 return @base+"."+@branchid
397 return @base+"."+@branchid
397 end
398 end
398 return @base
399 return @base
399 end
400 end
400
401
401 def isBranchRevision
402 def isBranchRevision
402 !@branchid.nil?
403 !@branchid.nil?
403 end
404 end
404
405
405 def prevRev
406 def prevRev
406 unless @revision == 0
407 unless @revision == 0
407 return buildRevision( @revision - 1 )
408 return buildRevision( @revision - 1 )
408 end
409 end
409 return buildRevision( @revision )
410 return buildRevision( @revision )
410 end
411 end
411
412
412 def is_in_branch_with_symbol(branch_symbol)
413 def is_in_branch_with_symbol(branch_symbol)
413 bpieces = branch_symbol.split(".")
414 bpieces = branch_symbol.split(".")
414 branch_start = "#{bpieces[0..-3].join(".")}.#{bpieces[-1]}"
415 branch_start = "#{bpieces[0..-3].join(".")}.#{bpieces[-1]}"
415 return ( branchVersion == branch_start )
416 return ( branchVersion == branch_start )
416 end
417 end
417
418
418 private
419 private
419 def buildRevision(rev)
420 def buildRevision(rev)
420 if rev == 0
421 if rev == 0
421 if @branchid.nil?
422 if @branchid.nil?
422 @base + ".0"
423 @base + ".0"
423 else
424 else
424 @base
425 @base
425 end
426 end
426 elsif @branchid.nil?
427 elsif @branchid.nil?
427 @base + "." + rev.to_s
428 @base + "." + rev.to_s
428 else
429 else
429 @base + "." + @branchid + "." + rev.to_s
430 @base + "." + @branchid + "." + rev.to_s
430 end
431 end
431 end
432 end
432
433
433 # Interpretiert die cvs revisionsnummern wie z.b. 1.14 oder 1.3.0.15
434 # Interpretiert die cvs revisionsnummern wie z.b. 1.14 oder 1.3.0.15
434 def parseRevision()
435 def parseRevision()
435 pieces = @complete_rev.split(".")
436 pieces = @complete_rev.split(".")
436 @revision = pieces.last.to_i
437 @revision = pieces.last.to_i
437 baseSize = 1
438 baseSize = 1
438 baseSize += (pieces.size / 2)
439 baseSize += (pieces.size / 2)
439 @base = pieces[0..-baseSize].join(".")
440 @base = pieces[0..-baseSize].join(".")
440 if baseSize > 2
441 if baseSize > 2
441 @branchid = pieces[-2]
442 @branchid = pieces[-2]
442 end
443 end
443 end
444 end
444 end
445 end
445 end
446 end
446 end
447 end
447 end
448 end
General Comments 0
You need to be logged in to leave comments. Login now