To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.
root / .svn / pristine / 39 / 39e07ef2d5632f8e7ca245cda7735bd6e7674f6d.svn-base @ 1298:4f746d8966dd
History | View | Annotate | Download (11.5 KB)
| 1 | 1295:622f24f53b42 | Chris | # Redmine - project management software |
|---|---|---|---|
| 2 | # Copyright (C) 2006-2013 Jean-Philippe Lang |
||
| 3 | # |
||
| 4 | # This program is free software; you can redistribute it and/or |
||
| 5 | # modify it under the terms of the GNU General Public License |
||
| 6 | # as published by the Free Software Foundation; either version 2 |
||
| 7 | # of the License, or (at your option) any later version. |
||
| 8 | # |
||
| 9 | # This program is distributed in the hope that it will be useful, |
||
| 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
| 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
| 12 | # GNU General Public License for more details. |
||
| 13 | # |
||
| 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 |
||
| 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||
| 17 | |||
| 18 | require 'redmine/scm/adapters/abstract_adapter' |
||
| 19 | |||
| 20 | module Redmine |
||
| 21 | module Scm |
||
| 22 | module Adapters |
||
| 23 | class BazaarAdapter < AbstractAdapter |
||
| 24 | |||
| 25 | # Bazaar executable name |
||
| 26 | BZR_BIN = Redmine::Configuration['scm_bazaar_command'] || "bzr" |
||
| 27 | |||
| 28 | class << self |
||
| 29 | def client_command |
||
| 30 | @@bin ||= BZR_BIN |
||
| 31 | end |
||
| 32 | |||
| 33 | def sq_bin |
||
| 34 | @@sq_bin ||= shell_quote_command |
||
| 35 | end |
||
| 36 | |||
| 37 | def client_version |
||
| 38 | @@client_version ||= (scm_command_version || []) |
||
| 39 | end |
||
| 40 | |||
| 41 | def client_available |
||
| 42 | !client_version.empty? |
||
| 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+)})
|
||
| 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 | |||
| 60 | def initialize(url, root_url=nil, login=nil, password=nil, path_encoding=nil) |
||
| 61 | @url = url |
||
| 62 | @root_url = url |
||
| 63 | @path_encoding = 'UTF-8' |
||
| 64 | # do not call *super* for non ASCII repository path |
||
| 65 | end |
||
| 66 | |||
| 67 | def bzr_path_encodig=(encoding) |
||
| 68 | @path_encoding = encoding |
||
| 69 | end |
||
| 70 | |||
| 71 | # Get info about the repository |
||
| 72 | def info |
||
| 73 | cmd_args = %w|revno| |
||
| 74 | cmd_args << bzr_target('')
|
||
| 75 | info = nil |
||
| 76 | scm_cmd(*cmd_args) do |io| |
||
| 77 | if io.read =~ %r{^(\d+)\r?$}
|
||
| 78 | info = Info.new({:root_url => url,
|
||
| 79 | :lastrev => Revision.new({
|
||
| 80 | :identifier => $1 |
||
| 81 | }) |
||
| 82 | }) |
||
| 83 | end |
||
| 84 | end |
||
| 85 | info |
||
| 86 | rescue ScmCommandAborted |
||
| 87 | return nil |
||
| 88 | end |
||
| 89 | |||
| 90 | # Returns an Entries collection |
||
| 91 | # or nil if the given path doesn't exist in the repository |
||
| 92 | def entries(path=nil, identifier=nil, options={})
|
||
| 93 | path ||= '' |
||
| 94 | entries = Entries.new |
||
| 95 | identifier = -1 unless identifier && identifier.to_i > 0 |
||
| 96 | cmd_args = %w|ls -v --show-ids| |
||
| 97 | cmd_args << "-r#{identifier.to_i}"
|
||
| 98 | cmd_args << bzr_target(path) |
||
| 99 | scm_cmd(*cmd_args) do |io| |
||
| 100 | prefix_utf8 = "#{url}/#{path}".gsub('\\', '/')
|
||
| 101 | logger.debug "PREFIX: #{prefix_utf8}"
|
||
| 102 | prefix = scm_iconv(@path_encoding, 'UTF-8', prefix_utf8) |
||
| 103 | prefix.force_encoding('ASCII-8BIT') if prefix.respond_to?(:force_encoding)
|
||
| 104 | re = %r{^V\s+(#{Regexp.escape(prefix)})?(\/?)([^\/]+)(\/?)\s+(\S+)\r?$}
|
||
| 105 | io.each_line do |line| |
||
| 106 | next unless line =~ re |
||
| 107 | name_locale, slash, revision = $3.strip, $4, $5.strip |
||
| 108 | name = scm_iconv('UTF-8', @path_encoding, name_locale)
|
||
| 109 | entries << Entry.new({:name => name,
|
||
| 110 | :path => ((path.empty? ? "" : "#{path}/") + name),
|
||
| 111 | :kind => (slash.blank? ? 'file' : 'dir'), |
||
| 112 | :size => nil, |
||
| 113 | :lastrev => Revision.new(:revision => revision) |
||
| 114 | }) |
||
| 115 | end |
||
| 116 | end |
||
| 117 | if logger && logger.debug? |
||
| 118 | logger.debug("Found #{entries.size} entries in the repository for #{target(path)}")
|
||
| 119 | end |
||
| 120 | entries.sort_by_name |
||
| 121 | rescue ScmCommandAborted |
||
| 122 | return nil |
||
| 123 | end |
||
| 124 | |||
| 125 | def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
|
||
| 126 | path ||= '' |
||
| 127 | identifier_from = (identifier_from and identifier_from.to_i > 0) ? identifier_from.to_i : 'last:1' |
||
| 128 | identifier_to = (identifier_to and identifier_to.to_i > 0) ? identifier_to.to_i : 1 |
||
| 129 | revisions = Revisions.new |
||
| 130 | cmd_args = %w|log -v --show-ids| |
||
| 131 | cmd_args << "-r#{identifier_to}..#{identifier_from}"
|
||
| 132 | cmd_args << bzr_target(path) |
||
| 133 | scm_cmd(*cmd_args) do |io| |
||
| 134 | revision = nil |
||
| 135 | parsing = nil |
||
| 136 | io.each_line do |line| |
||
| 137 | if line =~ /^----/ |
||
| 138 | revisions << revision if revision |
||
| 139 | revision = Revision.new(:paths => [], :message => '') |
||
| 140 | parsing = nil |
||
| 141 | else |
||
| 142 | next unless revision |
||
| 143 | if line =~ /^revno: (\d+)($|\s\[merge\]$)/ |
||
| 144 | revision.identifier = $1.to_i |
||
| 145 | elsif line =~ /^committer: (.+)$/ |
||
| 146 | revision.author = $1.strip |
||
| 147 | elsif line =~ /^revision-id:(.+)$/ |
||
| 148 | revision.scmid = $1.strip |
||
| 149 | elsif line =~ /^timestamp: (.+)$/ |
||
| 150 | revision.time = Time.parse($1).localtime |
||
| 151 | elsif line =~ /^ -----/ |
||
| 152 | # partial revisions |
||
| 153 | parsing = nil unless parsing == 'message' |
||
| 154 | elsif line =~ /^(message|added|modified|removed|renamed):/ |
||
| 155 | parsing = $1 |
||
| 156 | elsif line =~ /^ (.*)$/ |
||
| 157 | if parsing == 'message' |
||
| 158 | revision.message << "#{$1}\n"
|
||
| 159 | else |
||
| 160 | if $1 =~ /^(.*)\s+(\S+)$/ |
||
| 161 | path_locale = $1.strip |
||
| 162 | path = scm_iconv('UTF-8', @path_encoding, path_locale)
|
||
| 163 | revid = $2 |
||
| 164 | case parsing |
||
| 165 | when 'added' |
||
| 166 | revision.paths << {:action => 'A', :path => "/#{path}", :revision => revid}
|
||
| 167 | when 'modified' |
||
| 168 | revision.paths << {:action => 'M', :path => "/#{path}", :revision => revid}
|
||
| 169 | when 'removed' |
||
| 170 | revision.paths << {:action => 'D', :path => "/#{path}", :revision => revid}
|
||
| 171 | when 'renamed' |
||
| 172 | new_path = path.split('=>').last
|
||
| 173 | if new_path |
||
| 174 | revision.paths << {:action => 'M', :path => "/#{new_path.strip}",
|
||
| 175 | :revision => revid} |
||
| 176 | end |
||
| 177 | end |
||
| 178 | end |
||
| 179 | end |
||
| 180 | else |
||
| 181 | parsing = nil |
||
| 182 | end |
||
| 183 | end |
||
| 184 | end |
||
| 185 | revisions << revision if revision |
||
| 186 | end |
||
| 187 | revisions |
||
| 188 | rescue ScmCommandAborted |
||
| 189 | return nil |
||
| 190 | end |
||
| 191 | |||
| 192 | def diff(path, identifier_from, identifier_to=nil) |
||
| 193 | path ||= '' |
||
| 194 | if identifier_to |
||
| 195 | identifier_to = identifier_to.to_i |
||
| 196 | else |
||
| 197 | identifier_to = identifier_from.to_i - 1 |
||
| 198 | end |
||
| 199 | if identifier_from |
||
| 200 | identifier_from = identifier_from.to_i |
||
| 201 | end |
||
| 202 | diff = [] |
||
| 203 | cmd_args = %w|diff| |
||
| 204 | cmd_args << "-r#{identifier_to}..#{identifier_from}"
|
||
| 205 | cmd_args << bzr_target(path) |
||
| 206 | scm_cmd_no_raise(*cmd_args) do |io| |
||
| 207 | io.each_line do |line| |
||
| 208 | diff << line |
||
| 209 | end |
||
| 210 | end |
||
| 211 | diff |
||
| 212 | end |
||
| 213 | |||
| 214 | def cat(path, identifier=nil) |
||
| 215 | cat = nil |
||
| 216 | cmd_args = %w|cat| |
||
| 217 | cmd_args << "-r#{identifier.to_i}" if identifier && identifier.to_i > 0
|
||
| 218 | cmd_args << bzr_target(path) |
||
| 219 | scm_cmd(*cmd_args) do |io| |
||
| 220 | io.binmode |
||
| 221 | cat = io.read |
||
| 222 | end |
||
| 223 | cat |
||
| 224 | rescue ScmCommandAborted |
||
| 225 | return nil |
||
| 226 | end |
||
| 227 | |||
| 228 | def annotate(path, identifier=nil) |
||
| 229 | blame = Annotate.new |
||
| 230 | cmd_args = %w|annotate -q --all| |
||
| 231 | cmd_args << "-r#{identifier.to_i}" if identifier && identifier.to_i > 0
|
||
| 232 | cmd_args << bzr_target(path) |
||
| 233 | scm_cmd(*cmd_args) do |io| |
||
| 234 | author = nil |
||
| 235 | identifier = nil |
||
| 236 | io.each_line do |line| |
||
| 237 | next unless line =~ %r{^(\d+) ([^|]+)\| (.*)$}
|
||
| 238 | rev = $1 |
||
| 239 | blame.add_line($3.rstrip, |
||
| 240 | Revision.new( |
||
| 241 | :identifier => rev, |
||
| 242 | :revision => rev, |
||
| 243 | :author => $2.strip |
||
| 244 | )) |
||
| 245 | end |
||
| 246 | end |
||
| 247 | blame |
||
| 248 | rescue ScmCommandAborted |
||
| 249 | return nil |
||
| 250 | end |
||
| 251 | |||
| 252 | def self.branch_conf_path(path) |
||
| 253 | bcp = nil |
||
| 254 | m = path.match(%r{^(.*[/\\])\.bzr.*$})
|
||
| 255 | if m |
||
| 256 | bcp = m[1] |
||
| 257 | else |
||
| 258 | bcp = path |
||
| 259 | end |
||
| 260 | bcp.gsub!(%r{[\/\\]$}, "")
|
||
| 261 | if bcp |
||
| 262 | bcp = File.join(bcp, ".bzr", "branch", "branch.conf") |
||
| 263 | end |
||
| 264 | bcp |
||
| 265 | end |
||
| 266 | |||
| 267 | def append_revisions_only |
||
| 268 | return @aro if ! @aro.nil? |
||
| 269 | @aro = false |
||
| 270 | bcp = self.class.branch_conf_path(url) |
||
| 271 | if bcp && File.exist?(bcp) |
||
| 272 | begin |
||
| 273 | f = File::open(bcp, "r") |
||
| 274 | cnt = 0 |
||
| 275 | f.each_line do |line| |
||
| 276 | l = line.chomp.to_s |
||
| 277 | if l =~ /^\s*append_revisions_only\s*=\s*(\w+)\s*$/ |
||
| 278 | str_aro = $1 |
||
| 279 | if str_aro.upcase == "TRUE" |
||
| 280 | @aro = true |
||
| 281 | cnt += 1 |
||
| 282 | elsif str_aro.upcase == "FALSE" |
||
| 283 | @aro = false |
||
| 284 | cnt += 1 |
||
| 285 | end |
||
| 286 | if cnt > 1 |
||
| 287 | @aro = false |
||
| 288 | break |
||
| 289 | end |
||
| 290 | end |
||
| 291 | end |
||
| 292 | ensure |
||
| 293 | f.close |
||
| 294 | end |
||
| 295 | end |
||
| 296 | @aro |
||
| 297 | end |
||
| 298 | |||
| 299 | def scm_cmd(*args, &block) |
||
| 300 | full_args = [] |
||
| 301 | full_args += args |
||
| 302 | full_args_locale = [] |
||
| 303 | full_args.map do |e| |
||
| 304 | full_args_locale << scm_iconv(@path_encoding, 'UTF-8', e) |
||
| 305 | end |
||
| 306 | ret = shellout( |
||
| 307 | self.class.sq_bin + ' ' + |
||
| 308 | full_args_locale.map { |e| shell_quote e.to_s }.join(' '),
|
||
| 309 | &block |
||
| 310 | ) |
||
| 311 | if $? && $?.exitstatus != 0 |
||
| 312 | raise ScmCommandAborted, "bzr exited with non-zero status: #{$?.exitstatus}"
|
||
| 313 | end |
||
| 314 | ret |
||
| 315 | end |
||
| 316 | private :scm_cmd |
||
| 317 | |||
| 318 | def scm_cmd_no_raise(*args, &block) |
||
| 319 | full_args = [] |
||
| 320 | full_args += args |
||
| 321 | full_args_locale = [] |
||
| 322 | full_args.map do |e| |
||
| 323 | full_args_locale << scm_iconv(@path_encoding, 'UTF-8', e) |
||
| 324 | end |
||
| 325 | ret = shellout( |
||
| 326 | self.class.sq_bin + ' ' + |
||
| 327 | full_args_locale.map { |e| shell_quote e.to_s }.join(' '),
|
||
| 328 | &block |
||
| 329 | ) |
||
| 330 | ret |
||
| 331 | end |
||
| 332 | private :scm_cmd_no_raise |
||
| 333 | |||
| 334 | def bzr_target(path) |
||
| 335 | target(path, false) |
||
| 336 | end |
||
| 337 | private :bzr_target |
||
| 338 | end |
||
| 339 | end |
||
| 340 | end |
||
| 341 | end |