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 @ 441:cbce1fd3b1b7
History | View | Annotate | Download (8.2 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 | @@sq_bin ||= shell_quote(BZR_BIN) |
||
| 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 | 0:513646585e45 | Chris | # Get info about the repository
|
| 61 | def info |
||
| 62 | 245:051f544170fe | Chris | cmd = "#{self.class.sq_bin} revno #{target('')}"
|
| 63 | 0:513646585e45 | Chris | info = nil
|
| 64 | shellout(cmd) do |io|
|
||
| 65 | if io.read =~ %r{^(\d+)\r?$} |
||
| 66 | info = Info.new({:root_url => url, |
||
| 67 | :lastrev => Revision.new({ |
||
| 68 | :identifier => $1 |
||
| 69 | }) |
||
| 70 | }) |
||
| 71 | end
|
||
| 72 | end
|
||
| 73 | return nil if $? && $?.exitstatus != 0 |
||
| 74 | info |
||
| 75 | rescue CommandFailed |
||
| 76 | 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 | 245:051f544170fe | Chris | cmd = "#{self.class.sq_bin} ls -v --show-ids"
|
| 85 | 441:cbce1fd3b1b7 | Chris | identifier = -1 unless identifier && identifier.to_i > 0 |
| 86 | cmd << " -r#{identifier.to_i}"
|
||
| 87 | 0:513646585e45 | Chris | cmd << " #{target(path)}"
|
| 88 | shellout(cmd) 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 | return nil if $? && $?.exitstatus != 0 |
||
| 103 | logger.debug("Found #{entries.size} entries in the repository for #{target(path)}") if logger && logger.debug? |
||
| 104 | entries.sort_by_name |
||
| 105 | end
|
||
| 106 | 245:051f544170fe | Chris | |
| 107 | 0:513646585e45 | Chris | def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}) |
| 108 | path ||= ''
|
||
| 109 | 119:8661b858af72 | Chris | identifier_from = (identifier_from and identifier_from.to_i > 0) ? identifier_from.to_i : 'last:1' |
| 110 | identifier_to = (identifier_to and identifier_to.to_i > 0) ? identifier_to.to_i : 1 |
||
| 111 | 0:513646585e45 | Chris | revisions = Revisions.new
|
| 112 | 245:051f544170fe | Chris | cmd = "#{self.class.sq_bin} log -v --show-ids -r#{identifier_to}..#{identifier_from} #{target(path)}"
|
| 113 | 0:513646585e45 | Chris | shellout(cmd) do |io|
|
| 114 | revision = nil
|
||
| 115 | parsing = nil
|
||
| 116 | io.each_line do |line|
|
||
| 117 | if line =~ /^----/ |
||
| 118 | revisions << revision if revision
|
||
| 119 | revision = Revision.new(:paths => [], :message => '') |
||
| 120 | parsing = nil
|
||
| 121 | else
|
||
| 122 | next unless revision |
||
| 123 | if line =~ /^revno: (\d+)($|\s\[merge\]$)/ |
||
| 124 | revision.identifier = $1.to_i
|
||
| 125 | elsif line =~ /^committer: (.+)$/ |
||
| 126 | revision.author = $1.strip
|
||
| 127 | elsif line =~ /^revision-id:(.+)$/ |
||
| 128 | revision.scmid = $1.strip
|
||
| 129 | elsif line =~ /^timestamp: (.+)$/ |
||
| 130 | revision.time = Time.parse($1).localtime |
||
| 131 | elsif line =~ /^ -----/ |
||
| 132 | # partial revisions
|
||
| 133 | parsing = nil unless parsing == 'message' |
||
| 134 | elsif line =~ /^(message|added|modified|removed|renamed):/ |
||
| 135 | parsing = $1
|
||
| 136 | elsif line =~ /^ (.*)$/ |
||
| 137 | if parsing == 'message' |
||
| 138 | revision.message << "#{$1}\n"
|
||
| 139 | else
|
||
| 140 | if $1 =~ /^(.*)\s+(\S+)$/ |
||
| 141 | path = $1.strip
|
||
| 142 | revid = $2
|
||
| 143 | case parsing
|
||
| 144 | when 'added' |
||
| 145 | revision.paths << {:action => 'A', :path => "/#{path}", :revision => revid}
|
||
| 146 | when 'modified' |
||
| 147 | revision.paths << {:action => 'M', :path => "/#{path}", :revision => revid}
|
||
| 148 | when 'removed' |
||
| 149 | revision.paths << {:action => 'D', :path => "/#{path}", :revision => revid}
|
||
| 150 | when 'renamed' |
||
| 151 | new_path = path.split('=>').last
|
||
| 152 | revision.paths << {:action => 'M', :path => "/#{new_path.strip}", :revision => revid} if new_path
|
||
| 153 | end
|
||
| 154 | end
|
||
| 155 | end
|
||
| 156 | else
|
||
| 157 | parsing = nil
|
||
| 158 | end
|
||
| 159 | end
|
||
| 160 | end
|
||
| 161 | revisions << revision if revision
|
||
| 162 | end
|
||
| 163 | return nil if $? && $?.exitstatus != 0 |
||
| 164 | revisions |
||
| 165 | end
|
||
| 166 | 245:051f544170fe | Chris | |
| 167 | 0:513646585e45 | Chris | def diff(path, identifier_from, identifier_to=nil) |
| 168 | path ||= ''
|
||
| 169 | if identifier_to
|
||
| 170 | 441:cbce1fd3b1b7 | Chris | identifier_to = identifier_to.to_i |
| 171 | 0:513646585e45 | Chris | else
|
| 172 | identifier_to = identifier_from.to_i - 1
|
||
| 173 | end
|
||
| 174 | 119:8661b858af72 | Chris | if identifier_from
|
| 175 | identifier_from = identifier_from.to_i |
||
| 176 | end
|
||
| 177 | 245:051f544170fe | Chris | cmd = "#{self.class.sq_bin} diff -r#{identifier_to}..#{identifier_from} #{target(path)}"
|
| 178 | 0:513646585e45 | Chris | diff = [] |
| 179 | shellout(cmd) do |io|
|
||
| 180 | io.each_line do |line|
|
||
| 181 | diff << line |
||
| 182 | end
|
||
| 183 | end
|
||
| 184 | #return nil if $? && $?.exitstatus != 0
|
||
| 185 | diff |
||
| 186 | end
|
||
| 187 | 245:051f544170fe | Chris | |
| 188 | 0:513646585e45 | Chris | def cat(path, identifier=nil) |
| 189 | 245:051f544170fe | Chris | cmd = "#{self.class.sq_bin} cat"
|
| 190 | 0:513646585e45 | Chris | cmd << " -r#{identifier.to_i}" if identifier && identifier.to_i > 0 |
| 191 | cmd << " #{target(path)}"
|
||
| 192 | cat = nil
|
||
| 193 | shellout(cmd) do |io|
|
||
| 194 | io.binmode |
||
| 195 | cat = io.read |
||
| 196 | end
|
||
| 197 | return nil if $? && $?.exitstatus != 0 |
||
| 198 | cat |
||
| 199 | end
|
||
| 200 | 245:051f544170fe | Chris | |
| 201 | 0:513646585e45 | Chris | def annotate(path, identifier=nil) |
| 202 | 245:051f544170fe | Chris | cmd = "#{self.class.sq_bin} annotate --all"
|
| 203 | 0:513646585e45 | Chris | cmd << " -r#{identifier.to_i}" if identifier && identifier.to_i > 0 |
| 204 | cmd << " #{target(path)}"
|
||
| 205 | blame = Annotate.new
|
||
| 206 | shellout(cmd) do |io|
|
||
| 207 | author = nil
|
||
| 208 | identifier = nil
|
||
| 209 | io.each_line do |line|
|
||
| 210 | next unless line =~ %r{^(\d+) ([^|]+)\| (.*)$} |
||
| 211 | 441:cbce1fd3b1b7 | Chris | rev = $1
|
| 212 | blame.add_line($3.rstrip,
|
||
| 213 | Revision.new(
|
||
| 214 | :identifier => rev,
|
||
| 215 | :revision => rev,
|
||
| 216 | :author => $2.strip |
||
| 217 | )) |
||
| 218 | 0:513646585e45 | Chris | end
|
| 219 | end
|
||
| 220 | return nil if $? && $?.exitstatus != 0 |
||
| 221 | blame |
||
| 222 | end
|
||
| 223 | end
|
||
| 224 | end
|
||
| 225 | end
|
||
| 226 | end |