comparison lib/redmine/scm/adapters/subversion_adapter.rb @ 441:cbce1fd3b1b7 redmine-1.2

Update to Redmine 1.2-stable branch (Redmine SVN rev 6000)
author Chris Cannam
date Mon, 06 Jun 2011 14:24:13 +0100
parents 051f544170fe
children cbb26bc654de
comparison
equal deleted inserted replaced
245:051f544170fe 441:cbce1fd3b1b7
1 # Redmine - project management software 1 # Redmine - project management software
2 # Copyright (C) 2006-2010 Jean-Philippe Lang 2 # Copyright (C) 2006-2011 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'
38 def client_version 38 def client_version
39 @@client_version ||= (svn_binary_version || []) 39 @@client_version ||= (svn_binary_version || [])
40 end 40 end
41 41
42 def client_available 42 def client_available
43 !client_version.empty? 43 # --xml options are introduced in 1.3.
44 # http://subversion.apache.org/docs/release-notes/1.3.html
45 client_version_above?([1, 3])
44 end 46 end
45 47
46 def svn_binary_version 48 def svn_binary_version
47 scm_version = scm_version_from_command_line.dup 49 scm_version = scm_version_from_command_line.dup
48 if scm_version.respond_to?(:force_encoding) 50 if scm_version.respond_to?(:force_encoding)
68 if output.respond_to?(:force_encoding) 70 if output.respond_to?(:force_encoding)
69 output.force_encoding('UTF-8') 71 output.force_encoding('UTF-8')
70 end 72 end
71 begin 73 begin
72 doc = ActiveSupport::XmlMini.parse(output) 74 doc = ActiveSupport::XmlMini.parse(output)
73 #root_url = doc.elements["info/entry/repository/root"].text 75 # root_url = doc.elements["info/entry/repository/root"].text
74 info = Info.new({:root_url => doc['info']['entry']['repository']['root']['__content__'], 76 info = Info.new({:root_url => doc['info']['entry']['repository']['root']['__content__'],
75 :lastrev => Revision.new({ 77 :lastrev => Revision.new({
76 :identifier => doc['info']['entry']['commit']['revision'], 78 :identifier => doc['info']['entry']['commit']['revision'],
77 :time => Time.parse(doc['info']['entry']['commit']['date']['__content__']).localtime, 79 :time => Time.parse(doc['info']['entry']['commit']['date']['__content__']).localtime,
78 :author => (doc['info']['entry']['commit']['author'] ? doc['info']['entry']['commit']['author']['__content__'] : "") 80 :author => (doc['info']['entry']['commit']['author'] ? doc['info']['entry']['commit']['author']['__content__'] : "")
87 return nil 89 return nil
88 end 90 end
89 91
90 # Returns an Entries collection 92 # Returns an Entries collection
91 # 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
92 def entries(path=nil, identifier=nil) 94 def entries(path=nil, identifier=nil, options={})
93 path ||= '' 95 path ||= ''
94 identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD" 96 identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"
95 entries = Entries.new 97 entries = Entries.new
96 cmd = "#{self.class.sq_bin} list --xml #{target(path)}@#{identifier}" 98 cmd = "#{self.class.sq_bin} list --xml #{target(path)}@#{identifier}"
97 cmd << credentials_string 99 cmd << credentials_string
131 end 133 end
132 134
133 def properties(path, identifier=nil) 135 def properties(path, identifier=nil)
134 # proplist xml output supported in svn 1.5.0 and higher 136 # proplist xml output supported in svn 1.5.0 and higher
135 return nil unless self.class.client_version_above?([1, 5, 0]) 137 return nil unless self.class.client_version_above?([1, 5, 0])
136 138
137 identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD" 139 identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"
138 cmd = "#{self.class.sq_bin} proplist --verbose --xml #{target(path)}@#{identifier}" 140 cmd = "#{self.class.sq_bin} proplist --verbose --xml #{target(path)}@#{identifier}"
139 cmd << credentials_string 141 cmd << credentials_string
140 properties = {} 142 properties = {}
141 shellout(cmd) do |io| 143 shellout(cmd) do |io|
180 :from_path => path['copyfrom-path'], 182 :from_path => path['copyfrom-path'],
181 :from_revision => path['copyfrom-rev'] 183 :from_revision => path['copyfrom-rev']
182 } 184 }
183 end if logentry['paths'] && logentry['paths']['path'] 185 end if logentry['paths'] && logentry['paths']['path']
184 paths.sort! { |x,y| x[:path] <=> y[:path] } 186 paths.sort! { |x,y| x[:path] <=> y[:path] }
185 187
186 revisions << Revision.new({:identifier => logentry['revision'], 188 revisions << Revision.new({:identifier => logentry['revision'],
187 :author => (logentry['author'] ? logentry['author']['__content__'] : ""), 189 :author => (logentry['author'] ? logentry['author']['__content__'] : ""),
188 :time => Time.parse(logentry['date']['__content__'].to_s).localtime, 190 :time => Time.parse(logentry['date']['__content__'].to_s).localtime,
189 :message => logentry['msg']['__content__'], 191 :message => logentry['msg']['__content__'],
190 :paths => paths 192 :paths => paths
237 cmd << credentials_string 239 cmd << credentials_string
238 blame = Annotate.new 240 blame = Annotate.new
239 shellout(cmd) do |io| 241 shellout(cmd) do |io|
240 io.each_line do |line| 242 io.each_line do |line|
241 next unless line =~ %r{^\s*(\d+)\s*(\S+)\s(.*)$} 243 next unless line =~ %r{^\s*(\d+)\s*(\S+)\s(.*)$}
242 blame.add_line($3.rstrip, Revision.new(:identifier => $1.to_i, :author => $2.strip)) 244 rev = $1
245 blame.add_line($3.rstrip,
246 Revision.new(
247 :identifier => rev,
248 :revision => rev,
249 :author => $2.strip
250 ))
243 end 251 end
244 end 252 end
245 return nil if $? && $?.exitstatus != 0 253 return nil if $? && $?.exitstatus != 0
246 blame 254 blame
247 end 255 end
248 256
249 private 257 private
250 258
251 def credentials_string 259 def credentials_string
252 str = '' 260 str = ''
253 str << " --username #{shell_quote(@login)}" unless @login.blank? 261 str << " --username #{shell_quote(@login)}" unless @login.blank?
254 str << " --password #{shell_quote(@password)}" unless @login.blank? || @password.blank? 262 str << " --password #{shell_quote(@password)}" unless @login.blank? || @password.blank?
255 str << " --no-auth-cache --non-interactive" 263 str << " --no-auth-cache --non-interactive"
256 str 264 str
257 end 265 end
258 266
259 # Helper that iterates over the child elements of a xml node 267 # Helper that iterates over the child elements of a xml node
260 # MiniXml returns a hash when a single child is found or an array of hashes for multiple children 268 # MiniXml returns a hash when a single child is found
269 # or an array of hashes for multiple children
261 def each_xml_element(node, name) 270 def each_xml_element(node, name)
262 if node && node[name] 271 if node && node[name]
263 if node[name].is_a?(Hash) 272 if node[name].is_a?(Hash)
264 yield node[name] 273 yield node[name]
265 else 274 else