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 @ 912:5e80956cc792
History | View | Annotate | Download (10.4 KB)
| 1 | 441:cbce1fd3b1b7 | Chris | # Redmine - project management software
|
|---|---|---|---|
| 2 | # Copyright (C) 2006-2011 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 | require 'redmine/scm/adapters/abstract_adapter'
|
||
| 19 | |||
| 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 | 0:513646585e45 | Chris | # Get info about the repository
|
| 61 | def info |
||
| 62 | 909:cbb26bc654de | Chris | cmd_args = %w|revno|
|
| 63 | cmd_args << bzr_target('')
|
||
| 64 | 0:513646585e45 | Chris | info = nil
|
| 65 | 909:cbb26bc654de | Chris | scm_cmd(*cmd_args) do |io|
|
| 66 | 0:513646585e45 | Chris | if io.read =~ %r{^(\d+)\r?$} |
| 67 | info = Info.new({:root_url => url, |
||
| 68 | :lastrev => Revision.new({ |
||
| 69 | :identifier => $1 |
||
| 70 | }) |
||
| 71 | }) |
||
| 72 | end
|
||
| 73 | end
|
||
| 74 | info |
||
| 75 | 909:cbb26bc654de | Chris | rescue ScmCommandAborted |
| 76 | 0:513646585e45 | Chris | return nil |
| 77 | end
|
||
| 78 | 245:051f544170fe | Chris | |
| 79 | 0:513646585e45 | Chris | # Returns an Entries collection
|
| 80 | # or nil if the given path doesn't exist in the repository
|
||
| 81 | 441:cbce1fd3b1b7 | Chris | def entries(path=nil, identifier=nil, options={}) |
| 82 | 0:513646585e45 | Chris | path ||= ''
|
| 83 | entries = Entries.new
|
||
| 84 | 441:cbce1fd3b1b7 | Chris | identifier = -1 unless identifier && identifier.to_i > 0 |
| 85 | 909:cbb26bc654de | Chris | cmd_args = %w|ls -v --show-ids|
|
| 86 | cmd_args << "-r#{identifier.to_i}"
|
||
| 87 | cmd_args << bzr_target(path) |
||
| 88 | scm_cmd(*cmd_args) do |io|
|
||
| 89 | 0:513646585e45 | Chris | prefix = "#{url}/#{path}".gsub('\\', '/') |
| 90 | logger.debug "PREFIX: #{prefix}"
|
||
| 91 | re = %r{^V\s+(#{Regexp.escape(prefix)})?(\/?)([^\/]+)(\/?)\s+(\S+)\r?$}
|
||
| 92 | io.each_line do |line|
|
||
| 93 | next unless line =~ re |
||
| 94 | entries << Entry.new({:name => $3.strip, |
||
| 95 | :path => ((path.empty? ? "" : "#{path}/") + $3.strip), |
||
| 96 | :kind => ($4.blank? ? 'file' : 'dir'), |
||
| 97 | :size => nil, |
||
| 98 | :lastrev => Revision.new(:revision => $5.strip) |
||
| 99 | }) |
||
| 100 | end
|
||
| 101 | end
|
||
| 102 | 909:cbb26bc654de | Chris | if logger && logger.debug?
|
| 103 | logger.debug("Found #{entries.size} entries in the repository for #{target(path)}")
|
||
| 104 | end
|
||
| 105 | 0:513646585e45 | Chris | entries.sort_by_name |
| 106 | 909:cbb26bc654de | Chris | rescue ScmCommandAborted |
| 107 | return nil |
||
| 108 | 0:513646585e45 | Chris | end
|
| 109 | 245:051f544170fe | Chris | |
| 110 | 0:513646585e45 | Chris | def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}) |
| 111 | path ||= ''
|
||
| 112 | 119:8661b858af72 | Chris | identifier_from = (identifier_from and identifier_from.to_i > 0) ? identifier_from.to_i : 'last:1' |
| 113 | identifier_to = (identifier_to and identifier_to.to_i > 0) ? identifier_to.to_i : 1 |
||
| 114 | 0:513646585e45 | Chris | revisions = Revisions.new
|
| 115 | 909:cbb26bc654de | Chris | cmd_args = %w|log -v --show-ids|
|
| 116 | cmd_args << "-r#{identifier_to}..#{identifier_from}"
|
||
| 117 | cmd_args << bzr_target(path) |
||
| 118 | scm_cmd(*cmd_args) do |io|
|
||
| 119 | 0:513646585e45 | Chris | revision = nil
|
| 120 | 909:cbb26bc654de | Chris | parsing = nil
|
| 121 | 0:513646585e45 | Chris | io.each_line do |line|
|
| 122 | if line =~ /^----/ |
||
| 123 | revisions << revision if revision
|
||
| 124 | revision = Revision.new(:paths => [], :message => '') |
||
| 125 | parsing = nil
|
||
| 126 | else
|
||
| 127 | next unless revision |
||
| 128 | if line =~ /^revno: (\d+)($|\s\[merge\]$)/ |
||
| 129 | revision.identifier = $1.to_i
|
||
| 130 | elsif line =~ /^committer: (.+)$/ |
||
| 131 | revision.author = $1.strip
|
||
| 132 | elsif line =~ /^revision-id:(.+)$/ |
||
| 133 | revision.scmid = $1.strip
|
||
| 134 | elsif line =~ /^timestamp: (.+)$/ |
||
| 135 | revision.time = Time.parse($1).localtime |
||
| 136 | elsif line =~ /^ -----/ |
||
| 137 | # partial revisions
|
||
| 138 | parsing = nil unless parsing == 'message' |
||
| 139 | elsif line =~ /^(message|added|modified|removed|renamed):/ |
||
| 140 | parsing = $1
|
||
| 141 | elsif line =~ /^ (.*)$/ |
||
| 142 | if parsing == 'message' |
||
| 143 | revision.message << "#{$1}\n"
|
||
| 144 | else
|
||
| 145 | if $1 =~ /^(.*)\s+(\S+)$/ |
||
| 146 | path = $1.strip
|
||
| 147 | revid = $2
|
||
| 148 | case parsing
|
||
| 149 | when 'added' |
||
| 150 | revision.paths << {:action => 'A', :path => "/#{path}", :revision => revid}
|
||
| 151 | when 'modified' |
||
| 152 | revision.paths << {:action => 'M', :path => "/#{path}", :revision => revid}
|
||
| 153 | when 'removed' |
||
| 154 | revision.paths << {:action => 'D', :path => "/#{path}", :revision => revid}
|
||
| 155 | when 'renamed' |
||
| 156 | new_path = path.split('=>').last
|
||
| 157 | revision.paths << {:action => 'M', :path => "/#{new_path.strip}", :revision => revid} if new_path
|
||
| 158 | end
|
||
| 159 | end
|
||
| 160 | end
|
||
| 161 | else
|
||
| 162 | parsing = nil
|
||
| 163 | end
|
||
| 164 | end
|
||
| 165 | end
|
||
| 166 | revisions << revision if revision
|
||
| 167 | end
|
||
| 168 | revisions |
||
| 169 | 909:cbb26bc654de | Chris | rescue ScmCommandAborted |
| 170 | return nil |
||
| 171 | 0:513646585e45 | Chris | end
|
| 172 | 245:051f544170fe | Chris | |
| 173 | 0:513646585e45 | Chris | def diff(path, identifier_from, identifier_to=nil) |
| 174 | path ||= ''
|
||
| 175 | if identifier_to
|
||
| 176 | 441:cbce1fd3b1b7 | Chris | identifier_to = identifier_to.to_i |
| 177 | 0:513646585e45 | Chris | else
|
| 178 | identifier_to = identifier_from.to_i - 1
|
||
| 179 | end
|
||
| 180 | 119:8661b858af72 | Chris | if identifier_from
|
| 181 | identifier_from = identifier_from.to_i |
||
| 182 | end
|
||
| 183 | 0:513646585e45 | Chris | diff = [] |
| 184 | 909:cbb26bc654de | Chris | cmd_args = %w|diff|
|
| 185 | cmd_args << "-r#{identifier_to}..#{identifier_from}"
|
||
| 186 | cmd_args << bzr_target(path) |
||
| 187 | scm_cmd_no_raise(*cmd_args) do |io|
|
||
| 188 | 0:513646585e45 | Chris | io.each_line do |line|
|
| 189 | diff << line |
||
| 190 | end
|
||
| 191 | end
|
||
| 192 | diff |
||
| 193 | end
|
||
| 194 | 245:051f544170fe | Chris | |
| 195 | 0:513646585e45 | Chris | def cat(path, identifier=nil) |
| 196 | cat = nil
|
||
| 197 | 909:cbb26bc654de | Chris | cmd_args = %w|cat|
|
| 198 | cmd_args << "-r#{identifier.to_i}" if identifier && identifier.to_i > 0 |
||
| 199 | cmd_args << bzr_target(path) |
||
| 200 | scm_cmd(*cmd_args) do |io|
|
||
| 201 | 0:513646585e45 | Chris | io.binmode |
| 202 | cat = io.read |
||
| 203 | end
|
||
| 204 | cat |
||
| 205 | 909:cbb26bc654de | Chris | rescue ScmCommandAborted |
| 206 | return nil |
||
| 207 | 0:513646585e45 | Chris | end
|
| 208 | 245:051f544170fe | Chris | |
| 209 | 0:513646585e45 | Chris | def annotate(path, identifier=nil) |
| 210 | blame = Annotate.new
|
||
| 211 | 909:cbb26bc654de | Chris | cmd_args = %w|annotate -q --all|
|
| 212 | cmd_args << "-r#{identifier.to_i}" if identifier && identifier.to_i > 0 |
||
| 213 | cmd_args << bzr_target(path) |
||
| 214 | scm_cmd(*cmd_args) do |io|
|
||
| 215 | author = nil
|
||
| 216 | 0:513646585e45 | Chris | identifier = nil
|
| 217 | io.each_line do |line|
|
||
| 218 | next unless line =~ %r{^(\d+) ([^|]+)\| (.*)$} |
||
| 219 | 441:cbce1fd3b1b7 | Chris | rev = $1
|
| 220 | blame.add_line($3.rstrip,
|
||
| 221 | Revision.new(
|
||
| 222 | :identifier => rev,
|
||
| 223 | :revision => rev,
|
||
| 224 | :author => $2.strip |
||
| 225 | )) |
||
| 226 | 0:513646585e45 | Chris | end
|
| 227 | end
|
||
| 228 | blame |
||
| 229 | 909:cbb26bc654de | Chris | rescue ScmCommandAborted |
| 230 | return nil |
||
| 231 | 0:513646585e45 | Chris | end
|
| 232 | 909:cbb26bc654de | Chris | |
| 233 | def self.branch_conf_path(path) |
||
| 234 | bcp = nil
|
||
| 235 | m = path.match(%r{^(.*[/\\])\.bzr.*$})
|
||
| 236 | if m
|
||
| 237 | bcp = m[1]
|
||
| 238 | else
|
||
| 239 | bcp = path |
||
| 240 | end
|
||
| 241 | bcp.gsub!(%r{[\/\\]$}, "") |
||
| 242 | if bcp
|
||
| 243 | bcp = File.join(bcp, ".bzr", "branch", "branch.conf") |
||
| 244 | end
|
||
| 245 | bcp |
||
| 246 | end
|
||
| 247 | |||
| 248 | def append_revisions_only |
||
| 249 | return @aro if ! @aro.nil? |
||
| 250 | @aro = false |
||
| 251 | bcp = self.class.branch_conf_path(url)
|
||
| 252 | if bcp && File.exist?(bcp) |
||
| 253 | begin
|
||
| 254 | f = File::open(bcp, "r") |
||
| 255 | cnt = 0
|
||
| 256 | f.each_line do |line|
|
||
| 257 | l = line.chomp.to_s |
||
| 258 | if l =~ /^\s*append_revisions_only\s*=\s*(\w+)\s*$/ |
||
| 259 | str_aro = $1
|
||
| 260 | if str_aro.upcase == "TRUE" |
||
| 261 | @aro = true |
||
| 262 | cnt += 1
|
||
| 263 | elsif str_aro.upcase == "FALSE" |
||
| 264 | @aro = false |
||
| 265 | cnt += 1
|
||
| 266 | end
|
||
| 267 | if cnt > 1 |
||
| 268 | @aro = false |
||
| 269 | break
|
||
| 270 | end
|
||
| 271 | end
|
||
| 272 | end
|
||
| 273 | ensure
|
||
| 274 | f.close |
||
| 275 | end
|
||
| 276 | end
|
||
| 277 | @aro
|
||
| 278 | end
|
||
| 279 | |||
| 280 | def scm_cmd(*args, &block) |
||
| 281 | full_args = [] |
||
| 282 | full_args += args |
||
| 283 | ret = shellout( |
||
| 284 | self.class.sq_bin + ' ' + full_args.map { |e| shell_quote e.to_s }.join(' '), |
||
| 285 | &block |
||
| 286 | ) |
||
| 287 | if $? && $?.exitstatus != 0 |
||
| 288 | raise ScmCommandAborted, "bzr exited with non-zero status: #{$?.exitstatus}" |
||
| 289 | end
|
||
| 290 | ret |
||
| 291 | end
|
||
| 292 | private :scm_cmd
|
||
| 293 | |||
| 294 | def scm_cmd_no_raise(*args, &block) |
||
| 295 | full_args = [] |
||
| 296 | full_args += args |
||
| 297 | ret = shellout( |
||
| 298 | self.class.sq_bin + ' ' + full_args.map { |e| shell_quote e.to_s }.join(' '), |
||
| 299 | &block |
||
| 300 | ) |
||
| 301 | ret |
||
| 302 | end
|
||
| 303 | private :scm_cmd_no_raise
|
||
| 304 | |||
| 305 | def bzr_target(path) |
||
| 306 | target(path, false)
|
||
| 307 | end
|
||
| 308 | private :bzr_target
|
||
| 309 | 0:513646585e45 | Chris | end
|
| 310 | end
|
||
| 311 | end
|
||
| 312 | end |