File: lib/redmine/scm/adapters/bazaar_adapter.rb

Overview
Module Structure
Class Hierarchy
Code

Overview

Module Structure

  module: <Toplevel Module>
  module: Redmine#20
  module: Scm#21
  module: Adapters#22
  class: BazaarAdapter#23
inherits from
  AbstractAdapter ( Redmine::Scm::Adapters )
has properties
constant: BZR_BIN #26
class method: client_command #29
class method: sq_bin #33
class method: client_version #37
class method: client_available #41
class method: scm_command_version #45
class method: scm_version_from_command_line #55
method: info #61
method: entries / 3 #81
method: revisions / 4 #110
method: diff / 3 #173
method: cat / 2 #195
method: annotate / 2 #209
class method: branch_conf_path / 1 #233
method: append_revisions_only #248
method: scm_cmd / 2 #280
method: scm_cmd_no_raise / 2 #294
method: bzr_target / 1 #305

Class Hierarchy

Code

   1  # Redmine - project management software
   2  # Copyright (C) 2006-2011  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          # Get info about the repository
  61          def info
  62            cmd_args = %w|revno|
  63            cmd_args << bzr_target('')
  64            info = nil
  65            scm_cmd(*cmd_args) do |io|
  66              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          rescue ScmCommandAborted
  76            return nil
  77          end
  78 
  79          # Returns an Entries collection
  80          # or nil if the given path doesn't exist in the repository
  81          def entries(path=nil, identifier=nil, options={})
  82            path ||= ''
  83            entries = Entries.new
  84            identifier = -1 unless identifier && identifier.to_i > 0
  85            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              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            if logger && logger.debug?
 103              logger.debug("Found #{entries.size} entries in the repository for #{target(path)}")
 104            end
 105            entries.sort_by_name
 106          rescue ScmCommandAborted
 107            return nil
 108          end
 109 
 110          def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
 111            path ||= ''
 112            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            revisions = Revisions.new
 115            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              revision = nil
 120              parsing  = nil
 121              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          rescue ScmCommandAborted
 170            return nil
 171          end
 172 
 173          def diff(path, identifier_from, identifier_to=nil)
 174            path ||= ''
 175            if identifier_to
 176              identifier_to = identifier_to.to_i
 177            else
 178              identifier_to = identifier_from.to_i - 1
 179            end
 180            if identifier_from
 181              identifier_from = identifier_from.to_i
 182            end
 183            diff = []
 184            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              io.each_line do |line|
 189                diff << line
 190              end
 191            end
 192            diff
 193          end
 194 
 195          def cat(path, identifier=nil)
 196            cat = nil
 197            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              io.binmode
 202              cat = io.read
 203            end
 204            cat
 205          rescue ScmCommandAborted
 206            return nil
 207          end
 208 
 209          def annotate(path, identifier=nil)
 210            blame = Annotate.new
 211            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              identifier = nil
 217              io.each_line do |line|
 218                next unless line =~ %r{^(\d+) ([^|]+)\| (.*)$}
 219                rev = $1
 220                blame.add_line($3.rstrip,
 221                   Revision.new(
 222                    :identifier => rev,
 223                    :revision   => rev,
 224                    :author     => $2.strip
 225                    ))
 226              end
 227            end
 228            blame
 229          rescue ScmCommandAborted
 230            return nil
 231          end
 232 
 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        end
 310      end
 311    end
 312  end