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