Mercurial > hg > soundsoftware-site
comparison lib/redmine/scm/adapters/subversion_adapter.rb @ 245:051f544170fe
* Update to SVN trunk revision 4993
author | Chris Cannam |
---|---|
date | Thu, 03 Mar 2011 11:42:28 +0000 |
parents | 0579821a129a |
children | cbce1fd3b1b7 |
comparison
equal
deleted
inserted
replaced
244:8972b600f4fb | 245:051f544170fe |
---|---|
18 require 'redmine/scm/adapters/abstract_adapter' | 18 require 'redmine/scm/adapters/abstract_adapter' |
19 require 'uri' | 19 require 'uri' |
20 | 20 |
21 module Redmine | 21 module Redmine |
22 module Scm | 22 module Scm |
23 module Adapters | 23 module Adapters |
24 class SubversionAdapter < AbstractAdapter | 24 class SubversionAdapter < AbstractAdapter |
25 | 25 |
26 # SVN executable name | 26 # SVN executable name |
27 SVN_BIN = Redmine::Configuration['scm_subversion_command'] || "svn" | 27 SVN_BIN = Redmine::Configuration['scm_subversion_command'] || "svn" |
28 | 28 |
29 class << self | 29 class << self |
30 def client_command | |
31 @@bin ||= SVN_BIN | |
32 end | |
33 | |
34 def sq_bin | |
35 @@sq_bin ||= shell_quote(SVN_BIN) | |
36 end | |
37 | |
30 def client_version | 38 def client_version |
31 @@client_version ||= (svn_binary_version || []) | 39 @@client_version ||= (svn_binary_version || []) |
32 end | 40 end |
33 | 41 |
42 def client_available | |
43 !client_version.empty? | |
44 end | |
45 | |
34 def svn_binary_version | 46 def svn_binary_version |
35 cmd = "#{SVN_BIN} --version" | 47 scm_version = scm_version_from_command_line.dup |
36 version = nil | 48 if scm_version.respond_to?(:force_encoding) |
37 shellout(cmd) do |io| | 49 scm_version.force_encoding('ASCII-8BIT') |
38 # Read svn version in first returned line | 50 end |
39 if m = io.read.to_s.match(%r{\A(.*?)((\d+\.)+\d+)}) | 51 if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)}) |
40 version = m[2].scan(%r{\d+}).collect(&:to_i) | 52 m[2].scan(%r{\d+}).collect(&:to_i) |
41 end | 53 end |
42 end | 54 end |
43 return nil if $? && $?.exitstatus != 0 | 55 |
44 version | 56 def scm_version_from_command_line |
45 end | 57 shellout("#{sq_bin} --version") { |io| io.read }.to_s |
46 end | 58 end |
47 | 59 end |
60 | |
48 # Get info about the svn repository | 61 # Get info about the svn repository |
49 def info | 62 def info |
50 cmd = "#{SVN_BIN} info --xml #{target}" | 63 cmd = "#{self.class.sq_bin} info --xml #{target}" |
51 cmd << credentials_string | 64 cmd << credentials_string |
52 info = nil | 65 info = nil |
53 shellout(cmd) do |io| | 66 shellout(cmd) do |io| |
54 output = io.read | 67 output = io.read |
68 if output.respond_to?(:force_encoding) | |
69 output.force_encoding('UTF-8') | |
70 end | |
55 begin | 71 begin |
56 doc = ActiveSupport::XmlMini.parse(output) | 72 doc = ActiveSupport::XmlMini.parse(output) |
57 #root_url = doc.elements["info/entry/repository/root"].text | 73 #root_url = doc.elements["info/entry/repository/root"].text |
58 info = Info.new({:root_url => doc['info']['entry']['repository']['root']['__content__'], | 74 info = Info.new({:root_url => doc['info']['entry']['repository']['root']['__content__'], |
59 :lastrev => Revision.new({ | 75 :lastrev => Revision.new({ |
68 return nil if $? && $?.exitstatus != 0 | 84 return nil if $? && $?.exitstatus != 0 |
69 info | 85 info |
70 rescue CommandFailed | 86 rescue CommandFailed |
71 return nil | 87 return nil |
72 end | 88 end |
73 | 89 |
74 # Returns an Entries collection | 90 # Returns an Entries collection |
75 # or nil if the given path doesn't exist in the repository | 91 # or nil if the given path doesn't exist in the repository |
76 def entries(path=nil, identifier=nil) | 92 def entries(path=nil, identifier=nil) |
77 path ||= '' | 93 path ||= '' |
78 identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD" | 94 identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD" |
79 entries = Entries.new | 95 entries = Entries.new |
80 cmd = "#{SVN_BIN} list --xml #{target(path)}@#{identifier}" | 96 cmd = "#{self.class.sq_bin} list --xml #{target(path)}@#{identifier}" |
81 cmd << credentials_string | 97 cmd << credentials_string |
82 shellout(cmd) do |io| | 98 shellout(cmd) do |io| |
83 output = io.read | 99 output = io.read |
100 if output.respond_to?(:force_encoding) | |
101 output.force_encoding('UTF-8') | |
102 end | |
84 begin | 103 begin |
85 doc = ActiveSupport::XmlMini.parse(output) | 104 doc = ActiveSupport::XmlMini.parse(output) |
86 each_xml_element(doc['lists']['list'], 'entry') do |entry| | 105 each_xml_element(doc['lists']['list'], 'entry') do |entry| |
87 commit = entry['commit'] | 106 commit = entry['commit'] |
88 commit_date = commit['date'] | 107 commit_date = commit['date'] |
108 end | 127 end |
109 return nil if $? && $?.exitstatus != 0 | 128 return nil if $? && $?.exitstatus != 0 |
110 logger.debug("Found #{entries.size} entries in the repository for #{target(path)}") if logger && logger.debug? | 129 logger.debug("Found #{entries.size} entries in the repository for #{target(path)}") if logger && logger.debug? |
111 entries.sort_by_name | 130 entries.sort_by_name |
112 end | 131 end |
113 | 132 |
114 def properties(path, identifier=nil) | 133 def properties(path, identifier=nil) |
115 # proplist xml output supported in svn 1.5.0 and higher | 134 # proplist xml output supported in svn 1.5.0 and higher |
116 return nil unless self.class.client_version_above?([1, 5, 0]) | 135 return nil unless self.class.client_version_above?([1, 5, 0]) |
117 | 136 |
118 identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD" | 137 identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD" |
119 cmd = "#{SVN_BIN} proplist --verbose --xml #{target(path)}@#{identifier}" | 138 cmd = "#{self.class.sq_bin} proplist --verbose --xml #{target(path)}@#{identifier}" |
120 cmd << credentials_string | 139 cmd << credentials_string |
121 properties = {} | 140 properties = {} |
122 shellout(cmd) do |io| | 141 shellout(cmd) do |io| |
123 output = io.read | 142 output = io.read |
143 if output.respond_to?(:force_encoding) | |
144 output.force_encoding('UTF-8') | |
145 end | |
124 begin | 146 begin |
125 doc = ActiveSupport::XmlMini.parse(output) | 147 doc = ActiveSupport::XmlMini.parse(output) |
126 each_xml_element(doc['properties']['target'], 'property') do |property| | 148 each_xml_element(doc['properties']['target'], 'property') do |property| |
127 properties[ property['name'] ] = property['__content__'].to_s | 149 properties[ property['name'] ] = property['__content__'].to_s |
128 end | 150 end |
130 end | 152 end |
131 end | 153 end |
132 return nil if $? && $?.exitstatus != 0 | 154 return nil if $? && $?.exitstatus != 0 |
133 properties | 155 properties |
134 end | 156 end |
135 | 157 |
136 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}) | 158 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}) |
137 path ||= '' | 159 path ||= '' |
138 identifier_from = (identifier_from && identifier_from.to_i > 0) ? identifier_from.to_i : "HEAD" | 160 identifier_from = (identifier_from && identifier_from.to_i > 0) ? identifier_from.to_i : "HEAD" |
139 identifier_to = (identifier_to && identifier_to.to_i > 0) ? identifier_to.to_i : 1 | 161 identifier_to = (identifier_to && identifier_to.to_i > 0) ? identifier_to.to_i : 1 |
140 revisions = Revisions.new | 162 revisions = Revisions.new |
141 cmd = "#{SVN_BIN} log --xml -r #{identifier_from}:#{identifier_to}" | 163 cmd = "#{self.class.sq_bin} log --xml -r #{identifier_from}:#{identifier_to}" |
142 cmd << credentials_string | 164 cmd << credentials_string |
143 cmd << " --verbose " if options[:with_paths] | 165 cmd << " --verbose " if options[:with_paths] |
144 cmd << " --limit #{options[:limit].to_i}" if options[:limit] | 166 cmd << " --limit #{options[:limit].to_i}" if options[:limit] |
145 cmd << ' ' + target(path) | 167 cmd << ' ' + target(path) |
146 shellout(cmd) do |io| | 168 shellout(cmd) do |io| |
147 output = io.read | 169 output = io.read |
170 if output.respond_to?(:force_encoding) | |
171 output.force_encoding('UTF-8') | |
172 end | |
148 begin | 173 begin |
149 doc = ActiveSupport::XmlMini.parse(output) | 174 doc = ActiveSupport::XmlMini.parse(output) |
150 each_xml_element(doc['log'], 'logentry') do |logentry| | 175 each_xml_element(doc['log'], 'logentry') do |logentry| |
151 paths = [] | 176 paths = [] |
152 each_xml_element(logentry['paths'], 'path') do |path| | 177 each_xml_element(logentry['paths'], 'path') do |path| |
169 end | 194 end |
170 end | 195 end |
171 return nil if $? && $?.exitstatus != 0 | 196 return nil if $? && $?.exitstatus != 0 |
172 revisions | 197 revisions |
173 end | 198 end |
174 | 199 |
175 def diff(path, identifier_from, identifier_to=nil, type="inline") | 200 def diff(path, identifier_from, identifier_to=nil, type="inline") |
176 path ||= '' | 201 path ||= '' |
177 identifier_from = (identifier_from and identifier_from.to_i > 0) ? identifier_from.to_i : '' | 202 identifier_from = (identifier_from and identifier_from.to_i > 0) ? identifier_from.to_i : '' |
203 | |
178 identifier_to = (identifier_to and identifier_to.to_i > 0) ? identifier_to.to_i : (identifier_from.to_i - 1) | 204 identifier_to = (identifier_to and identifier_to.to_i > 0) ? identifier_to.to_i : (identifier_from.to_i - 1) |
179 | 205 |
180 cmd = "#{SVN_BIN} diff -r " | 206 cmd = "#{self.class.sq_bin} diff -r " |
181 cmd << "#{identifier_to}:" | 207 cmd << "#{identifier_to}:" |
182 cmd << "#{identifier_from}" | 208 cmd << "#{identifier_from}" |
183 cmd << " #{target(path)}@#{identifier_from}" | 209 cmd << " #{target(path)}@#{identifier_from}" |
184 cmd << credentials_string | 210 cmd << credentials_string |
185 diff = [] | 211 diff = [] |
189 end | 215 end |
190 end | 216 end |
191 return nil if $? && $?.exitstatus != 0 | 217 return nil if $? && $?.exitstatus != 0 |
192 diff | 218 diff |
193 end | 219 end |
194 | 220 |
195 def cat(path, identifier=nil) | 221 def cat(path, identifier=nil) |
196 identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD" | 222 identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD" |
197 cmd = "#{SVN_BIN} cat #{target(path)}@#{identifier}" | 223 cmd = "#{self.class.sq_bin} cat #{target(path)}@#{identifier}" |
198 cmd << credentials_string | 224 cmd << credentials_string |
199 cat = nil | 225 cat = nil |
200 shellout(cmd) do |io| | 226 shellout(cmd) do |io| |
201 io.binmode | 227 io.binmode |
202 cat = io.read | 228 cat = io.read |
203 end | 229 end |
204 return nil if $? && $?.exitstatus != 0 | 230 return nil if $? && $?.exitstatus != 0 |
205 cat | 231 cat |
206 end | 232 end |
207 | 233 |
208 def annotate(path, identifier=nil) | 234 def annotate(path, identifier=nil) |
209 identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD" | 235 identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD" |
210 cmd = "#{SVN_BIN} blame #{target(path)}@#{identifier}" | 236 cmd = "#{self.class.sq_bin} blame #{target(path)}@#{identifier}" |
211 cmd << credentials_string | 237 cmd << credentials_string |
212 blame = Annotate.new | 238 blame = Annotate.new |
213 shellout(cmd) do |io| | 239 shellout(cmd) do |io| |
214 io.each_line do |line| | 240 io.each_line do |line| |
215 next unless line =~ %r{^\s*(\d+)\s*(\S+)\s(.*)$} | 241 next unless line =~ %r{^\s*(\d+)\s*(\S+)\s(.*)$} |