To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.
root / lib / redmine / scm / adapters / bazaar_adapter.rb @ 1298:4f746d8966dd
History | View | Annotate | Download (11.5 KB)
| 1 | 441:cbce1fd3b1b7 | Chris | # Redmine - project management software
|
|---|---|---|---|
| 2 | 1295:622f24f53b42 | Chris | # Copyright (C) 2006-2013 Jean-Philippe Lang
|
| 3 | 0:513646585e45 | Chris | #
|
| 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 | 441:cbce1fd3b1b7 | Chris | #
|
| 9 | 0:513646585e45 | Chris | # 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 | 441:cbce1fd3b1b7 | Chris | #
|
| 14 | 0:513646585e45 | Chris | # 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 | 1136:51d7f3e06556 | chris | require_dependency 'redmine/scm/adapters/abstract_adapter'
|
| 19 | 0:513646585e45 | Chris | |
| 20 | module Redmine |
||
| 21 | module Scm |
||
| 22 | 245:051f544170fe | Chris | module Adapters |
| 23 | 0:513646585e45 | Chris | class BazaarAdapter < AbstractAdapter |
| 24 | 245:051f544170fe | Chris | |
| 25 | 0:513646585e45 | Chris | # Bazaar executable name
|
| 26 | 210:0579821a129a | Chris | BZR_BIN = Redmine::Configuration['scm_bazaar_command'] || "bzr" |
| 27 | 245:051f544170fe | Chris | |
| 28 | class << self |
||
| 29 | def client_command |
||
| 30 | @@bin ||= BZR_BIN |
||
| 31 | end
|
||
| 32 | |||
| 33 | def sq_bin |
||
| 34 | 909:cbb26bc654de | Chris | @@sq_bin ||= shell_quote_command
|
| 35 | 245:051f544170fe | Chris | 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 | 1115:433d4f72a19b | Chris | 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 | 0:513646585e45 | Chris | # Get info about the repository
|
| 72 | def info |
||
| 73 | 909:cbb26bc654de | Chris | cmd_args = %w|revno|
|
| 74 | cmd_args << bzr_target('')
|
||
| 75 | 0:513646585e45 | Chris | info = nil
|
| 76 | 909:cbb26bc654de | Chris | scm_cmd(*cmd_args) do |io|
|
| 77 | 0:513646585e45 | Chris | 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 | 909:cbb26bc654de | Chris | rescue ScmCommandAborted |
| 87 | 0:513646585e45 | Chris | return nil |
| 88 | end
|
||
| 89 | 245:051f544170fe | Chris | |
| 90 | 0:513646585e45 | Chris | # Returns an Entries collection
|
| 91 | # or nil if the given path doesn't exist in the repository
|
||
| 92 | 441:cbce1fd3b1b7 | Chris | def entries(path=nil, identifier=nil, options={}) |
| 93 | 0:513646585e45 | Chris | path ||= ''
|
| 94 | entries = Entries.new
|
||
| 95 | 441:cbce1fd3b1b7 | Chris | identifier = -1 unless identifier && identifier.to_i > 0 |
| 96 | 909:cbb26bc654de | Chris | 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 | 1115:433d4f72a19b | Chris | 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 | 0:513646585e45 | Chris | re = %r{^V\s+(#{Regexp.escape(prefix)})?(\/?)([^\/]+)(\/?)\s+(\S+)\r?$}
|
| 105 | io.each_line do |line|
|
||
| 106 | next unless line =~ re |
||
| 107 | 1295:622f24f53b42 | Chris | name_locale, slash, revision = $3.strip, $4, $5.strip |
| 108 | 1115:433d4f72a19b | Chris | name = scm_iconv('UTF-8', @path_encoding, name_locale) |
| 109 | entries << Entry.new({:name => name, |
||
| 110 | :path => ((path.empty? ? "" : "#{path}/") + name), |
||
| 111 | 1295:622f24f53b42 | Chris | :kind => (slash.blank? ? 'file' : 'dir'), |
| 112 | 0:513646585e45 | Chris | :size => nil, |
| 113 | 1295:622f24f53b42 | Chris | :lastrev => Revision.new(:revision => revision) |
| 114 | 0:513646585e45 | Chris | }) |
| 115 | end
|
||
| 116 | end
|
||
| 117 | 909:cbb26bc654de | Chris | if logger && logger.debug?
|
| 118 | logger.debug("Found #{entries.size} entries in the repository for #{target(path)}")
|
||
| 119 | end
|
||
| 120 | 0:513646585e45 | Chris | entries.sort_by_name |
| 121 | 909:cbb26bc654de | Chris | rescue ScmCommandAborted |
| 122 | return nil |
||
| 123 | 0:513646585e45 | Chris | end
|
| 124 | 245:051f544170fe | Chris | |
| 125 | 0:513646585e45 | Chris | def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}) |
| 126 | path ||= ''
|
||
| 127 | 117:af80e5618e9b | Chris | 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 | 0:513646585e45 | Chris | revisions = Revisions.new
|
| 130 | 909:cbb26bc654de | Chris | 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 | 0:513646585e45 | Chris | revision = nil
|
| 135 | 909:cbb26bc654de | Chris | parsing = nil
|
| 136 | 0:513646585e45 | Chris | 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 | 1115:433d4f72a19b | Chris | path_locale = $1.strip
|
| 162 | path = scm_iconv('UTF-8', @path_encoding, path_locale) |
||
| 163 | 0:513646585e45 | Chris | 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 | 1115:433d4f72a19b | Chris | if new_path
|
| 174 | revision.paths << {:action => 'M', :path => "/#{new_path.strip}",
|
||
| 175 | :revision => revid}
|
||
| 176 | end
|
||
| 177 | 0:513646585e45 | Chris | 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 | 909:cbb26bc654de | Chris | rescue ScmCommandAborted |
| 189 | return nil |
||
| 190 | 0:513646585e45 | Chris | end
|
| 191 | 245:051f544170fe | Chris | |
| 192 | 0:513646585e45 | Chris | def diff(path, identifier_from, identifier_to=nil) |
| 193 | path ||= ''
|
||
| 194 | if identifier_to
|
||
| 195 | 441:cbce1fd3b1b7 | Chris | identifier_to = identifier_to.to_i |
| 196 | 0:513646585e45 | Chris | else
|
| 197 | identifier_to = identifier_from.to_i - 1
|
||
| 198 | end
|
||
| 199 | 117:af80e5618e9b | Chris | if identifier_from
|
| 200 | identifier_from = identifier_from.to_i |
||
| 201 | end
|
||
| 202 | 0:513646585e45 | Chris | diff = [] |
| 203 | 909:cbb26bc654de | Chris | 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 | 0:513646585e45 | Chris | io.each_line do |line|
|
| 208 | diff << line |
||
| 209 | end
|
||
| 210 | end
|
||
| 211 | diff |
||
| 212 | end
|
||
| 213 | 245:051f544170fe | Chris | |
| 214 | 0:513646585e45 | Chris | def cat(path, identifier=nil) |
| 215 | cat = nil
|
||
| 216 | 909:cbb26bc654de | Chris | 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 | 0:513646585e45 | Chris | io.binmode |
| 221 | cat = io.read |
||
| 222 | end
|
||
| 223 | cat |
||
| 224 | 909:cbb26bc654de | Chris | rescue ScmCommandAborted |
| 225 | return nil |
||
| 226 | 0:513646585e45 | Chris | end
|
| 227 | 245:051f544170fe | Chris | |
| 228 | 0:513646585e45 | Chris | def annotate(path, identifier=nil) |
| 229 | blame = Annotate.new
|
||
| 230 | 909:cbb26bc654de | Chris | 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 | 0:513646585e45 | Chris | identifier = nil
|
| 236 | io.each_line do |line|
|
||
| 237 | next unless line =~ %r{^(\d+) ([^|]+)\| (.*)$} |
||
| 238 | 441:cbce1fd3b1b7 | Chris | rev = $1
|
| 239 | blame.add_line($3.rstrip,
|
||
| 240 | Revision.new(
|
||
| 241 | :identifier => rev,
|
||
| 242 | :revision => rev,
|
||
| 243 | :author => $2.strip |
||
| 244 | )) |
||
| 245 | 0:513646585e45 | Chris | end
|
| 246 | end
|
||
| 247 | blame |
||
| 248 | 909:cbb26bc654de | Chris | rescue ScmCommandAborted |
| 249 | return nil |
||
| 250 | 0:513646585e45 | Chris | end
|
| 251 | 909:cbb26bc654de | Chris | |
| 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 | 1115:433d4f72a19b | Chris | full_args_locale = [] |
| 303 | full_args.map do |e|
|
||
| 304 | full_args_locale << scm_iconv(@path_encoding, 'UTF-8', e) |
||
| 305 | end
|
||
| 306 | 909:cbb26bc654de | Chris | ret = shellout( |
| 307 | 1115:433d4f72a19b | Chris | self.class.sq_bin + ' ' + |
| 308 | full_args_locale.map { |e| shell_quote e.to_s }.join(' '),
|
||
| 309 | 909:cbb26bc654de | Chris | &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 | 1115:433d4f72a19b | Chris | full_args_locale = [] |
| 322 | full_args.map do |e|
|
||
| 323 | full_args_locale << scm_iconv(@path_encoding, 'UTF-8', e) |
||
| 324 | end
|
||
| 325 | 909:cbb26bc654de | Chris | ret = shellout( |
| 326 | 1115:433d4f72a19b | Chris | self.class.sq_bin + ' ' + |
| 327 | full_args_locale.map { |e| shell_quote e.to_s }.join(' '),
|
||
| 328 | 909:cbb26bc654de | Chris | &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 | 0:513646585e45 | Chris | end
|
| 339 | end
|
||
| 340 | end
|
||
| 341 | end |