Mercurial > hg > soundsoftware-site
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 |