comparison lib/redmine/scm/adapters/.svn/text-base/cvs_adapter.rb.svn-base @ 245:051f544170fe

* Update to SVN trunk revision 4993
author Chris Cannam
date Thu, 03 Mar 2011 11:42:28 +0000
parents 0579821a129a
children eeebe205a056 cbce1fd3b1b7
comparison
equal deleted inserted replaced
244:8972b600f4fb 245:051f544170fe
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 class << self
29 def client_command
30 @@bin ||= CVS_BIN
31 end
32
33 def sq_bin
34 @@sq_bin ||= shell_quote(CVS_BIN)
35 end
36
37 def client_version
38 @@client_version ||= (scm_command_version || [])
39 end
40
41 def client_available
42 client_version_above?([1, 12])
43 end
44
45 def scm_command_version
46 scm_version = scm_version_from_command_line.dup
47 if scm_version.respond_to?(:force_encoding)
48 scm_version.force_encoding('ASCII-8BIT')
49 end
50 if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)}m)
51 m[2].scan(%r{\d+}).collect(&:to_i)
52 end
53 end
54
55 def scm_version_from_command_line
56 shellout("#{sq_bin} --version") { |io| io.read }.to_s
57 end
58 end
59
28 # Guidelines for the input: 60 # Guidelines for the input:
29 # url -> the project-path, relative to the cvsroot (eg. module name) 61 # url -> the project-path, relative to the cvsroot (eg. module name)
30 # root_url -> the good old, sometimes damned, CVSROOT 62 # root_url -> the good old, sometimes damned, CVSROOT
31 # login -> unnecessary 63 # login -> unnecessary
32 # password -> unnecessary too 64 # password -> unnecessary too
33 def initialize(url, root_url=nil, login=nil, password=nil) 65 def initialize(url, root_url=nil, login=nil, password=nil,
66 path_encoding=nil)
34 @url = url 67 @url = url
35 @login = login if login && !login.empty? 68 @login = login if login && !login.empty?
36 @password = (password || "") if @login 69 @password = (password || "") if @login
37 #TODO: better Exception here (IllegalArgumentException) 70 #TODO: better Exception here (IllegalArgumentException)
38 raise CommandFailed if root_url.blank? 71 raise CommandFailed if root_url.blank?
39 @root_url = root_url 72 @root_url = root_url
40 end 73 end
41 74
42 def root_url 75 def root_url
43 @root_url 76 @root_url
44 end 77 end
45 78
46 def url 79 def url
47 @url 80 @url
48 end 81 end
49 82
50 def info 83 def info
51 logger.debug "<cvs> info" 84 logger.debug "<cvs> info"
52 Info.new({:root_url => @root_url, :lastrev => nil}) 85 Info.new({:root_url => @root_url, :lastrev => nil})
53 end 86 end
54 87
55 def get_previous_revision(revision) 88 def get_previous_revision(revision)
56 CvsRevisionHelper.new(revision).prevRev 89 CvsRevisionHelper.new(revision).prevRev
57 end 90 end
58 91
59 # Returns an Entries collection 92 # Returns an Entries collection
60 # or nil if the given path doesn't exist in the repository 93 # or nil if the given path doesn't exist in the repository
61 # this method is used by the repository-browser (aka LIST) 94 # this method is used by the repository-browser (aka LIST)
62 def entries(path=nil, identifier=nil) 95 def entries(path=nil, identifier=nil)
63 logger.debug "<cvs> entries '#{path}' with identifier '#{identifier}'" 96 logger.debug "<cvs> entries '#{path}' with identifier '#{identifier}'"
64 path_with_project="#{url}#{with_leading_slash(path)}" 97 path_with_project="#{url}#{with_leading_slash(path)}"
65 entries = Entries.new 98 entries = Entries.new
66 cmd = "#{CVS_BIN} -d #{shell_quote root_url} rls -e" 99 cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rls -e"
67 cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier 100 cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier
68 cmd << " #{shell_quote path_with_project}" 101 cmd << " #{shell_quote path_with_project}"
69 shellout(cmd) do |io| 102 shellout(cmd) do |io|
70 io.each_line(){|line| 103 io.each_line(){|line|
71 fields=line.chop.split('/',-1) 104 fields=line.chop.split('/',-1)
72 logger.debug(">>InspectLine #{fields.inspect}") 105 logger.debug(">>InspectLine #{fields.inspect}")
73 106
74 if fields[0]!="D" 107 if fields[0]!="D"
75 entries << Entry.new({:name => fields[-5], 108 entries << Entry.new({:name => fields[-5],
76 #:path => fields[-4].include?(path)?fields[-4]:(path + "/"+ fields[-4]), 109 #:path => fields[-4].include?(path)?fields[-4]:(path + "/"+ fields[-4]),
77 :path => "#{path}/#{fields[-5]}", 110 :path => "#{path}/#{fields[-5]}",
78 :kind => 'file', 111 :kind => 'file',
94 end 127 end
95 } 128 }
96 end 129 end
97 return nil if $? && $?.exitstatus != 0 130 return nil if $? && $?.exitstatus != 0
98 entries.sort_by_name 131 entries.sort_by_name
99 end 132 end
100 133
101 STARTLOG="----------------------------" 134 STARTLOG="----------------------------"
102 ENDLOG ="=============================================================================" 135 ENDLOG ="============================================================================="
103 136
104 # Returns all revisions found between identifier_from and identifier_to 137 # Returns all revisions found between identifier_from and identifier_to
105 # in the repository. both identifier have to be dates or nil. 138 # in the repository. both identifier have to be dates or nil.
106 # these method returns nothing but yield every result in block 139 # these method returns nothing but yield every result in block
107 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}, &block) 140 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}, &block)
108 logger.debug "<cvs> revisions path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}" 141 logger.debug "<cvs> revisions path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
109 142
110 path_with_project="#{url}#{with_leading_slash(path)}" 143 path_with_project="#{url}#{with_leading_slash(path)}"
111 cmd = "#{CVS_BIN} -d #{shell_quote root_url} rlog" 144 cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rlog"
112 cmd << " -d\">#{time_to_cvstime_rlog(identifier_from)}\"" if identifier_from 145 cmd << " -d\">#{time_to_cvstime_rlog(identifier_from)}\"" if identifier_from
113 cmd << " #{shell_quote path_with_project}" 146 cmd << " #{shell_quote path_with_project}"
114 shellout(cmd) do |io| 147 shellout(cmd) do |io|
115 state="entry_start" 148 state="entry_start"
116 149
121 entry_path=nil 154 entry_path=nil
122 entry_name=nil 155 entry_name=nil
123 file_state=nil 156 file_state=nil
124 branch_map=nil 157 branch_map=nil
125 158
126 io.each_line() do |line| 159 io.each_line() do |line|
127 160
128 if state!="revision" && /^#{ENDLOG}/ =~ line 161 if state!="revision" && /^#{ENDLOG}/ =~ line
129 commit_log=String.new 162 commit_log=String.new
130 revision=nil 163 revision=nil
131 state="entry_start" 164 state="entry_start"
160 elsif /^#{ENDLOG}/ =~ line 193 elsif /^#{ENDLOG}/ =~ line
161 state="head" 194 state="head"
162 end 195 end
163 next 196 next
164 elsif state=="revision" 197 elsif state=="revision"
165 if /^#{ENDLOG}/ =~ line || /^#{STARTLOG}/ =~ line 198 if /^#{ENDLOG}/ =~ line || /^#{STARTLOG}/ =~ line
166 if revision 199 if revision
167 200
168 revHelper=CvsRevisionHelper.new(revision) 201 revHelper=CvsRevisionHelper.new(revision)
169 revBranch="HEAD" 202 revBranch="HEAD"
170 203
171 branch_map.each() do |branch_name,branch_point| 204 branch_map.each() do |branch_name,branch_point|
172 if revHelper.is_in_branch_with_symbol(branch_point) 205 if revHelper.is_in_branch_with_symbol(branch_point)
174 end 207 end
175 end 208 end
176 209
177 logger.debug("********** YIELD Revision #{revision}::#{revBranch}") 210 logger.debug("********** YIELD Revision #{revision}::#{revBranch}")
178 211
179 yield Revision.new({ 212 yield Revision.new({
180 :time => date, 213 :time => date,
181 :author => author, 214 :author => author,
182 :message=>commit_log.chomp, 215 :message=>commit_log.chomp,
183 :paths => [{ 216 :paths => [{
184 :revision => revision, 217 :revision => revision,
186 :path=>entry_path, 219 :path=>entry_path,
187 :name=>entry_name, 220 :name=>entry_name,
188 :kind=>'file', 221 :kind=>'file',
189 :action=>file_state 222 :action=>file_state
190 }] 223 }]
191 }) 224 })
192 end 225 end
193 226
194 commit_log=String.new 227 commit_log=String.new
195 revision=nil 228 revision=nil
196 229
197 if /^#{ENDLOG}/ =~ line 230 if /^#{ENDLOG}/ =~ line
198 state="entry_start" 231 state="entry_start"
199 end 232 end
200 next 233 next
201 end 234 end
202 235
203 if /^branches: (.+)$/ =~ line 236 if /^branches: (.+)$/ =~ line
204 #TODO: version.branch = $1 237 #TODO: version.branch = $1
205 elsif /^revision (\d+(?:\.\d+)+).*$/ =~ line 238 elsif /^revision (\d+(?:\.\d+)+).*$/ =~ line
206 revision = $1 239 revision = $1
207 elsif /^date:\s+(\d+.\d+.\d+\s+\d+:\d+:\d+)/ =~ line 240 elsif /^date:\s+(\d+.\d+.\d+\s+\d+:\d+:\d+)/ =~ line
214 # unless linechanges.nil? 247 # unless linechanges.nil?
215 # version.line_plus = linechanges[1] 248 # version.line_plus = linechanges[1]
216 # version.line_minus = linechanges[2] 249 # version.line_minus = linechanges[2]
217 # else 250 # else
218 # version.line_plus = 0 251 # version.line_plus = 0
219 # version.line_minus = 0 252 # version.line_minus = 0
220 # end 253 # end
221 else 254 else
222 commit_log << line unless line =~ /^\*\*\* empty log message \*\*\*/ 255 commit_log << line unless line =~ /^\*\*\* empty log message \*\*\*/
223 end 256 end
224 end 257 end
225 end 258 end
226 end 259 end
227 end 260 end
228 261
229 def diff(path, identifier_from, identifier_to=nil) 262 def diff(path, identifier_from, identifier_to=nil)
230 logger.debug "<cvs> diff path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}" 263 logger.debug "<cvs> diff path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
231 path_with_project="#{url}#{with_leading_slash(path)}" 264 path_with_project="#{url}#{with_leading_slash(path)}"
232 cmd = "#{CVS_BIN} -d #{shell_quote root_url} rdiff -u -r#{identifier_to} -r#{identifier_from} #{shell_quote path_with_project}" 265 cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rdiff -u -r#{identifier_to} -r#{identifier_from} #{shell_quote path_with_project}"
233 diff = [] 266 diff = []
234 shellout(cmd) do |io| 267 shellout(cmd) do |io|
235 io.each_line do |line| 268 io.each_line do |line|
236 diff << line 269 diff << line
237 end 270 end
238 end 271 end
239 return nil if $? && $?.exitstatus != 0 272 return nil if $? && $?.exitstatus != 0
240 diff 273 diff
241 end 274 end
242 275
243 def cat(path, identifier=nil) 276 def cat(path, identifier=nil)
244 identifier = (identifier) ? identifier : "HEAD" 277 identifier = (identifier) ? identifier : "HEAD"
245 logger.debug "<cvs> cat path:'#{path}',identifier #{identifier}" 278 logger.debug "<cvs> cat path:'#{path}',identifier #{identifier}"
246 path_with_project="#{url}#{with_leading_slash(path)}" 279 path_with_project="#{url}#{with_leading_slash(path)}"
247 cmd = "#{CVS_BIN} -d #{shell_quote root_url} co" 280 cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} co"
248 cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier 281 cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier
249 cmd << " -p #{shell_quote path_with_project}" 282 cmd << " -p #{shell_quote path_with_project}"
250 cat = nil 283 cat = nil
251 shellout(cmd) do |io| 284 shellout(cmd) do |io|
285 io.binmode
252 cat = io.read 286 cat = io.read
253 end 287 end
254 return nil if $? && $?.exitstatus != 0 288 return nil if $? && $?.exitstatus != 0
255 cat 289 cat
256 end 290 end
257 291
258 def annotate(path, identifier=nil) 292 def annotate(path, identifier=nil)
259 identifier = (identifier) ? identifier.to_i : "HEAD" 293 identifier = (identifier) ? identifier.to_i : "HEAD"
260 logger.debug "<cvs> annotate path:'#{path}',identifier #{identifier}" 294 logger.debug "<cvs> annotate path:'#{path}',identifier #{identifier}"
261 path_with_project="#{url}#{with_leading_slash(path)}" 295 path_with_project="#{url}#{with_leading_slash(path)}"
262 cmd = "#{CVS_BIN} -d #{shell_quote root_url} rannotate -r#{identifier} #{shell_quote path_with_project}" 296 cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rannotate -r#{identifier} #{shell_quote path_with_project}"
263 blame = Annotate.new 297 blame = Annotate.new
264 shellout(cmd) do |io| 298 shellout(cmd) do |io|
265 io.each_line do |line| 299 io.each_line do |line|
266 next unless line =~ %r{^([\d\.]+)\s+\(([^\)]+)\s+[^\)]+\):\s(.*)$} 300 next unless line =~ %r{^([\d\.]+)\s+\(([^\)]+)\s+[^\)]+\):\s(.*)$}
267 blame.add_line($3.rstrip, Revision.new(:revision => $1, :author => $2.strip)) 301 blame.add_line($3.rstrip, Revision.new(:revision => $1, :author => $2.strip))
268 end 302 end
269 end 303 end
270 return nil if $? && $?.exitstatus != 0 304 return nil if $? && $?.exitstatus != 0
271 blame 305 blame
272 end 306 end
273 307
274 private 308 private
275 309
276 # Returns the root url without the connexion string 310 # Returns the root url without the connexion string
277 # :pserver:anonymous@foo.bar:/path => /path 311 # :pserver:anonymous@foo.bar:/path => /path
278 # :ext:cvsservername:/path => /path 312 # :ext:cvsservername:/path => /path
279 def root_url_path 313 def root_url_path
280 root_url.to_s.gsub(/^:.+:\d*/, '') 314 root_url.to_s.gsub(/^:.+:\d*/, '')
343 end 377 end
344 378
345 private 379 private
346 def buildRevision(rev) 380 def buildRevision(rev)
347 if rev== 0 381 if rev== 0
348 @base 382 if @branchid.nil?
383 @base+".0"
384 else
385 @base
386 end
349 elsif @branchid.nil? 387 elsif @branchid.nil?
350 @base+"."+rev.to_s 388 @base+"."+rev.to_s
351 else 389 else
352 @base+"."+@branchid+"."+rev.to_s 390 @base+"."+@branchid+"."+rev.to_s
353 end 391 end