Revision 441:cbce1fd3b1b7 lib/redmine/scm/adapters/.svn/text-base
| lib/redmine/scm/adapters/.svn/text-base/abstract_adapter.rb.svn-base | ||
|---|---|---|
| 1 |
# redMine - project management software
|
|
| 2 |
# Copyright (C) 2006-2007 Jean-Philippe Lang
|
|
| 1 |
# Redmine - project management software
|
|
| 2 |
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| 6 | 6 |
# as published by the Free Software Foundation; either version 2 |
| 7 | 7 |
# of the License, or (at your option) any later version. |
| 8 |
#
|
|
| 8 |
# |
|
| 9 | 9 |
# This program is distributed in the hope that it will be useful, |
| 10 | 10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | 11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | 12 |
# GNU General Public License for more details. |
| 13 |
#
|
|
| 13 |
# |
|
| 14 | 14 |
# You should have received a copy of the GNU General Public License |
| 15 | 15 |
# along with this program; if not, write to the Free Software |
| 16 | 16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| ... | ... | |
| 90 | 90 |
def url |
| 91 | 91 |
@url |
| 92 | 92 |
end |
| 93 |
|
|
| 93 |
|
|
| 94 |
def path_encoding |
|
| 95 |
nil |
|
| 96 |
end |
|
| 97 |
|
|
| 94 | 98 |
# get info about the svn repository |
| 95 | 99 |
def info |
| 96 | 100 |
return nil |
| 97 | 101 |
end |
| 98 |
|
|
| 102 |
|
|
| 99 | 103 |
# Returns the entry identified by path and revision identifier |
| 100 | 104 |
# or nil if entry doesn't exist in the repository |
| 101 | 105 |
def entry(path=nil, identifier=nil) |
| ... | ... | |
| 111 | 115 |
es ? es.detect {|e| e.name == search_name} : nil
|
| 112 | 116 |
end |
| 113 | 117 |
end |
| 114 |
|
|
| 118 |
|
|
| 115 | 119 |
# Returns an Entries collection |
| 116 | 120 |
# or nil if the given path doesn't exist in the repository |
| 117 |
def entries(path=nil, identifier=nil) |
|
| 121 |
def entries(path=nil, identifier=nil, options={})
|
|
| 118 | 122 |
return nil |
| 119 | 123 |
end |
| 120 | 124 |
|
| ... | ... | |
| 122 | 126 |
return nil |
| 123 | 127 |
end |
| 124 | 128 |
|
| 125 |
def tags
|
|
| 129 |
def tags |
|
| 126 | 130 |
return nil |
| 127 | 131 |
end |
| 128 | 132 |
|
| 129 | 133 |
def default_branch |
| 130 | 134 |
return nil |
| 131 | 135 |
end |
| 132 |
|
|
| 136 |
|
|
| 133 | 137 |
def properties(path, identifier=nil) |
| 134 | 138 |
return nil |
| 135 | 139 |
end |
| 136 |
|
|
| 140 |
|
|
| 137 | 141 |
def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
|
| 138 | 142 |
return nil |
| 139 | 143 |
end |
| 140 |
|
|
| 144 |
|
|
| 141 | 145 |
def diff(path, identifier_from, identifier_to=nil) |
| 142 | 146 |
return nil |
| 143 | 147 |
end |
| 144 |
|
|
| 148 |
|
|
| 145 | 149 |
def cat(path, identifier=nil) |
| 146 | 150 |
return nil |
| 147 | 151 |
end |
| 148 |
|
|
| 152 |
|
|
| 149 | 153 |
def with_leading_slash(path) |
| 150 | 154 |
path ||= '' |
| 151 | 155 |
(path[0,1]!="/") ? "/#{path}" : path
|
| ... | ... | |
| 175 | 179 |
info = self.info |
| 176 | 180 |
info ? info.root_url : nil |
| 177 | 181 |
end |
| 178 |
|
|
| 182 |
|
|
| 179 | 183 |
def target(path) |
| 180 | 184 |
path ||= '' |
| 181 | 185 |
base = path.match(/^\//) ? root_url : url |
| ... | ... | |
| 223 | 227 |
q = (Redmine::Platform.mswin? ? '"' : "'") |
| 224 | 228 |
cmd.to_s.gsub(/(\-\-(password|username))\s+(#{q}[^#{q}]+#{q}|[^#{q}]\S+)/, '\\1 xxxx')
|
| 225 | 229 |
end |
| 226 |
|
|
| 230 |
|
|
| 227 | 231 |
def strip_credential(cmd) |
| 228 | 232 |
self.class.strip_credential(cmd) |
| 229 | 233 |
end |
| ... | ... | |
| 242 | 246 |
|
| 243 | 247 |
class Entries < Array |
| 244 | 248 |
def sort_by_name |
| 245 |
sort {|x,y|
|
|
| 249 |
sort {|x,y|
|
|
| 246 | 250 |
if x.kind == y.kind |
| 247 | 251 |
x.name.to_s <=> y.name.to_s |
| 248 | 252 |
else |
| ... | ... | |
| 250 | 254 |
end |
| 251 | 255 |
} |
| 252 | 256 |
end |
| 253 |
|
|
| 257 |
|
|
| 254 | 258 |
def revisions |
| 255 | 259 |
revisions ||= Revisions.new(collect{|entry| entry.lastrev}.compact)
|
| 256 | 260 |
end |
| 257 | 261 |
end |
| 258 |
|
|
| 262 |
|
|
| 259 | 263 |
class Info |
| 260 | 264 |
attr_accessor :root_url, :lastrev |
| 261 | 265 |
def initialize(attributes={})
|
| ... | ... | |
| 263 | 267 |
self.lastrev = attributes[:lastrev] |
| 264 | 268 |
end |
| 265 | 269 |
end |
| 266 |
|
|
| 270 |
|
|
| 267 | 271 |
class Entry |
| 268 | 272 |
attr_accessor :name, :path, :kind, :size, :lastrev |
| 269 | 273 |
def initialize(attributes={})
|
| ... | ... | |
| 273 | 277 |
self.size = attributes[:size].to_i if attributes[:size] |
| 274 | 278 |
self.lastrev = attributes[:lastrev] |
| 275 | 279 |
end |
| 276 |
|
|
| 280 |
|
|
| 277 | 281 |
def is_file? |
| 278 | 282 |
'file' == self.kind |
| 279 | 283 |
end |
| 280 |
|
|
| 284 |
|
|
| 281 | 285 |
def is_dir? |
| 282 | 286 |
'dir' == self.kind |
| 283 | 287 |
end |
| 284 |
|
|
| 288 |
|
|
| 285 | 289 |
def is_text? |
| 286 | 290 |
Redmine::MimeType.is_type?('text', name)
|
| 287 | 291 |
end |
| 288 | 292 |
end |
| 289 |
|
|
| 293 |
|
|
| 290 | 294 |
class Revisions < Array |
| 291 | 295 |
def latest |
| 292 | 296 |
sort {|x,y|
|
| ... | ... | |
| 296 | 300 |
0 |
| 297 | 301 |
end |
| 298 | 302 |
}.last |
| 299 |
end
|
|
| 303 |
end |
|
| 300 | 304 |
end |
| 301 |
|
|
| 305 |
|
|
| 302 | 306 |
class Revision |
| 303 |
attr_accessor :scmid, :name, :author, :time, :message, :paths, :revision, :branch
|
|
| 304 |
attr_writer :identifier
|
|
| 307 |
attr_accessor :scmid, :name, :author, :time, :message, |
|
| 308 |
:paths, :revision, :branch, :identifier
|
|
| 305 | 309 |
|
| 306 | 310 |
def initialize(attributes={})
|
| 307 | 311 |
self.identifier = attributes[:identifier] |
| 308 |
self.scmid = attributes[:scmid] |
|
| 309 |
self.name = attributes[:name] || self.identifier |
|
| 310 |
self.author = attributes[:author] |
|
| 311 |
self.time = attributes[:time] |
|
| 312 |
self.message = attributes[:message] || "" |
|
| 313 |
self.paths = attributes[:paths] |
|
| 314 |
self.revision = attributes[:revision] |
|
| 315 |
self.branch = attributes[:branch] |
|
| 316 |
end |
|
| 317 |
|
|
| 318 |
# Returns the identifier of this revision; see also Changeset model |
|
| 319 |
def identifier |
|
| 320 |
(@identifier || revision).to_s |
|
| 312 |
self.scmid = attributes[:scmid] |
|
| 313 |
self.name = attributes[:name] || self.identifier |
|
| 314 |
self.author = attributes[:author] |
|
| 315 |
self.time = attributes[:time] |
|
| 316 |
self.message = attributes[:message] || "" |
|
| 317 |
self.paths = attributes[:paths] |
|
| 318 |
self.revision = attributes[:revision] |
|
| 319 |
self.branch = attributes[:branch] |
|
| 321 | 320 |
end |
| 322 | 321 |
|
| 323 | 322 |
# Returns the readable identifier. |
| 324 | 323 |
def format_identifier |
| 325 |
identifier
|
|
| 324 |
self.identifier.to_s
|
|
| 326 | 325 |
end |
| 327 | 326 |
end |
| 328 | 327 |
|
| 329 | 328 |
class Annotate |
| 330 | 329 |
attr_reader :lines, :revisions |
| 331 |
|
|
| 330 |
|
|
| 332 | 331 |
def initialize |
| 333 | 332 |
@lines = [] |
| 334 | 333 |
@revisions = [] |
| 335 | 334 |
end |
| 336 |
|
|
| 335 |
|
|
| 337 | 336 |
def add_line(line, revision) |
| 338 | 337 |
@lines << line |
| 339 | 338 |
@revisions << revision |
| 340 | 339 |
end |
| 341 |
|
|
| 340 |
|
|
| 342 | 341 |
def content |
| 343 | 342 |
content = lines.join("\n")
|
| 344 | 343 |
end |
| 345 |
|
|
| 344 |
|
|
| 346 | 345 |
def empty? |
| 347 | 346 |
lines.empty? |
| 348 | 347 |
end |
| lib/redmine/scm/adapters/.svn/text-base/bazaar_adapter.rb.svn-base | ||
|---|---|---|
| 1 |
# redMine - project management software
|
|
| 2 |
# Copyright (C) 2006-2007 Jean-Philippe Lang
|
|
| 1 |
# Redmine - project management software
|
|
| 2 |
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| 6 | 6 |
# as published by the Free Software Foundation; either version 2 |
| 7 | 7 |
# of the License, or (at your option) any later version. |
| 8 |
#
|
|
| 8 |
# |
|
| 9 | 9 |
# This program is distributed in the hope that it will be useful, |
| 10 | 10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | 11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | 12 |
# GNU General Public License for more details. |
| 13 |
#
|
|
| 13 |
# |
|
| 14 | 14 |
# You should have received a copy of the GNU General Public License |
| 15 | 15 |
# along with this program; if not, write to the Free Software |
| 16 | 16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| ... | ... | |
| 78 | 78 |
|
| 79 | 79 |
# Returns an Entries collection |
| 80 | 80 |
# or nil if the given path doesn't exist in the repository |
| 81 |
def entries(path=nil, identifier=nil) |
|
| 81 |
def entries(path=nil, identifier=nil, options={})
|
|
| 82 | 82 |
path ||= '' |
| 83 | 83 |
entries = Entries.new |
| 84 | 84 |
cmd = "#{self.class.sq_bin} ls -v --show-ids"
|
| 85 |
identifier = -1 unless identifier && identifier.to_i > 0
|
|
| 86 |
cmd << " -r#{identifier.to_i}"
|
|
| 85 |
identifier = -1 unless identifier && identifier.to_i > 0 |
|
| 86 |
cmd << " -r#{identifier.to_i}"
|
|
| 87 | 87 |
cmd << " #{target(path)}"
|
| 88 | 88 |
shellout(cmd) do |io| |
| 89 | 89 |
prefix = "#{url}/#{path}".gsub('\\', '/')
|
| ... | ... | |
| 120 | 120 |
parsing = nil |
| 121 | 121 |
else |
| 122 | 122 |
next unless revision |
| 123 |
|
|
| 124 | 123 |
if line =~ /^revno: (\d+)($|\s\[merge\]$)/ |
| 125 | 124 |
revision.identifier = $1.to_i |
| 126 | 125 |
elsif line =~ /^committer: (.+)$/ |
| ... | ... | |
| 168 | 167 |
def diff(path, identifier_from, identifier_to=nil) |
| 169 | 168 |
path ||= '' |
| 170 | 169 |
if identifier_to |
| 171 |
identifier_to = identifier_to.to_i
|
|
| 170 |
identifier_to = identifier_to.to_i |
|
| 172 | 171 |
else |
| 173 | 172 |
identifier_to = identifier_from.to_i - 1 |
| 174 | 173 |
end |
| ... | ... | |
| 209 | 208 |
identifier = nil |
| 210 | 209 |
io.each_line do |line| |
| 211 | 210 |
next unless line =~ %r{^(\d+) ([^|]+)\| (.*)$}
|
| 212 |
blame.add_line($3.rstrip, Revision.new(:identifier => $1.to_i, :author => $2.strip)) |
|
| 211 |
rev = $1 |
|
| 212 |
blame.add_line($3.rstrip, |
|
| 213 |
Revision.new( |
|
| 214 |
:identifier => rev, |
|
| 215 |
:revision => rev, |
|
| 216 |
:author => $2.strip |
|
| 217 |
)) |
|
| 213 | 218 |
end |
| 214 | 219 |
end |
| 215 | 220 |
return nil if $? && $?.exitstatus != 0 |
| lib/redmine/scm/adapters/.svn/text-base/cvs_adapter.rb.svn-base | ||
|---|---|---|
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| 6 | 6 |
# as published by the Free Software Foundation; either version 2 |
| 7 | 7 |
# of the License, or (at your option) any later version. |
| 8 |
#
|
|
| 8 |
# |
|
| 9 | 9 |
# This program is distributed in the hope that it will be useful, |
| 10 | 10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | 11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | 12 |
# GNU General Public License for more details. |
| 13 |
#
|
|
| 13 |
# |
|
| 14 | 14 |
# You should have received a copy of the GNU General Public License |
| 15 | 15 |
# along with this program; if not, write to the Free Software |
| 16 | 16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| ... | ... | |
| 25 | 25 |
# CVS executable name |
| 26 | 26 |
CVS_BIN = Redmine::Configuration['scm_cvs_command'] || "cvs" |
| 27 | 27 |
|
| 28 |
# raised if scm command exited with error, e.g. unknown revision. |
|
| 29 |
class ScmCommandAborted < CommandFailed; end |
|
| 30 |
|
|
| 28 | 31 |
class << self |
| 29 | 32 |
def client_command |
| 30 | 33 |
@@bin ||= CVS_BIN |
| ... | ... | |
| 58 | 61 |
end |
| 59 | 62 |
|
| 60 | 63 |
# Guidelines for the input: |
| 61 |
# url -> the project-path, relative to the cvsroot (eg. module name) |
|
| 64 |
# url -> the project-path, relative to the cvsroot (eg. module name)
|
|
| 62 | 65 |
# root_url -> the good old, sometimes damned, CVSROOT |
| 63 |
# login -> unnecessary |
|
| 66 |
# login -> unnecessary
|
|
| 64 | 67 |
# password -> unnecessary too |
| 65 | 68 |
def initialize(url, root_url=nil, login=nil, password=nil, |
| 66 | 69 |
path_encoding=nil) |
| 67 |
@url = url |
|
| 68 |
@login = login if login && !login.empty? |
|
| 70 |
@path_encoding = path_encoding.blank? ? 'UTF-8' : path_encoding |
|
| 71 |
@url = url |
|
| 72 |
# TODO: better Exception here (IllegalArgumentException) |
|
| 73 |
raise CommandFailed if root_url.blank? |
|
| 74 |
@root_url = root_url |
|
| 75 |
|
|
| 76 |
# These are unused. |
|
| 77 |
@login = login if login && !login.empty? |
|
| 69 | 78 |
@password = (password || "") if @login |
| 70 |
#TODO: better Exception here (IllegalArgumentException) |
|
| 71 |
raise CommandFailed if root_url.blank? |
|
| 72 |
@root_url = root_url |
|
| 73 | 79 |
end |
| 74 | 80 |
|
| 75 |
def root_url |
|
| 76 |
@root_url |
|
| 77 |
end |
|
| 78 |
|
|
| 79 |
def url |
|
| 80 |
@url |
|
| 81 |
def path_encoding |
|
| 82 |
@path_encoding |
|
| 81 | 83 |
end |
| 82 | 84 |
|
| 83 | 85 |
def info |
| ... | ... | |
| 92 | 94 |
# Returns an Entries collection |
| 93 | 95 |
# or nil if the given path doesn't exist in the repository |
| 94 | 96 |
# this method is used by the repository-browser (aka LIST) |
| 95 |
def entries(path=nil, identifier=nil) |
|
| 97 |
def entries(path=nil, identifier=nil, options={})
|
|
| 96 | 98 |
logger.debug "<cvs> entries '#{path}' with identifier '#{identifier}'"
|
| 97 |
path_with_project="#{url}#{with_leading_slash(path)}"
|
|
| 99 |
path_locale = scm_iconv(@path_encoding, 'UTF-8', path) |
|
| 100 |
path_locale.force_encoding("ASCII-8BIT") if path_locale.respond_to?(:force_encoding)
|
|
| 98 | 101 |
entries = Entries.new |
| 99 |
cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rls -e"
|
|
| 100 |
cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier
|
|
| 101 |
cmd << " #{shell_quote path_with_project}"
|
|
| 102 |
shellout(cmd) do |io|
|
|
| 103 |
io.each_line(){|line|
|
|
| 104 |
fields=line.chop.split('/',-1)
|
|
| 102 |
cmd_args = %w|-q rls -e|
|
|
| 103 |
cmd_args << "-D" << time_to_cvstime_rlog(identifier) if identifier
|
|
| 104 |
cmd_args << path_with_proj(path)
|
|
| 105 |
scm_cmd(*cmd_args) do |io|
|
|
| 106 |
io.each_line() do |line|
|
|
| 107 |
fields = line.chop.split('/',-1)
|
|
| 105 | 108 |
logger.debug(">>InspectLine #{fields.inspect}")
|
| 106 |
|
|
| 107 | 109 |
if fields[0]!="D" |
| 108 |
entries << Entry.new({:name => fields[-5],
|
|
| 110 |
time = nil |
|
| 111 |
# Thu Dec 13 16:27:22 2007 |
|
| 112 |
time_l = fields[-3].split(' ')
|
|
| 113 |
if time_l.size == 5 && time_l[4].length == 4 |
|
| 114 |
begin |
|
| 115 |
time = Time.parse( |
|
| 116 |
"#{time_l[1]} #{time_l[2]} #{time_l[3]} GMT #{time_l[4]}")
|
|
| 117 |
rescue |
|
| 118 |
end |
|
| 119 |
end |
|
| 120 |
entries << Entry.new( |
|
| 121 |
{
|
|
| 122 |
:name => scm_iconv('UTF-8', @path_encoding, fields[-5]),
|
|
| 109 | 123 |
#:path => fields[-4].include?(path)?fields[-4]:(path + "/"+ fields[-4]), |
| 110 |
:path => "#{path}/#{fields[-5]}",
|
|
| 124 |
:path => scm_iconv('UTF-8', @path_encoding, "#{path_locale}/#{fields[-5]}"),
|
|
| 111 | 125 |
:kind => 'file', |
| 112 | 126 |
:size => nil, |
| 113 |
:lastrev => Revision.new({
|
|
| 114 |
:revision => fields[-4], |
|
| 115 |
:name => fields[-4], |
|
| 116 |
:time => Time.parse(fields[-3]), |
|
| 117 |
:author => '' |
|
| 127 |
:lastrev => Revision.new( |
|
| 128 |
{
|
|
| 129 |
:revision => fields[-4], |
|
| 130 |
:name => scm_iconv('UTF-8', @path_encoding, fields[-4]),
|
|
| 131 |
:time => time, |
|
| 132 |
:author => '' |
|
| 133 |
}) |
|
| 118 | 134 |
}) |
| 119 |
}) |
|
| 120 | 135 |
else |
| 121 |
entries << Entry.new({:name => fields[1],
|
|
| 122 |
:path => "#{path}/#{fields[1]}",
|
|
| 123 |
:kind => 'dir', |
|
| 124 |
:size => nil, |
|
| 136 |
entries << Entry.new( |
|
| 137 |
{
|
|
| 138 |
:name => scm_iconv('UTF-8', @path_encoding, fields[1]),
|
|
| 139 |
:path => scm_iconv('UTF-8', @path_encoding, "#{path_locale}/#{fields[1]}"),
|
|
| 140 |
:kind => 'dir', |
|
| 141 |
:size => nil, |
|
| 125 | 142 |
:lastrev => nil |
| 126 |
}) |
|
| 143 |
})
|
|
| 127 | 144 |
end |
| 128 |
}
|
|
| 145 |
end
|
|
| 129 | 146 |
end |
| 130 |
return nil if $? && $?.exitstatus != 0 |
|
| 131 | 147 |
entries.sort_by_name |
| 148 |
rescue ScmCommandAborted |
|
| 149 |
nil |
|
| 132 | 150 |
end |
| 133 | 151 |
|
| 134 | 152 |
STARTLOG="----------------------------" |
| ... | ... | |
| 138 | 156 |
# in the repository. both identifier have to be dates or nil. |
| 139 | 157 |
# these method returns nothing but yield every result in block |
| 140 | 158 |
def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}, &block)
|
| 141 |
logger.debug "<cvs> revisions path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
|
|
| 142 |
|
|
| 143 |
path_with_project="#{url}#{with_leading_slash(path)}"
|
|
| 144 |
cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rlog"
|
|
| 145 |
cmd << " -d\">#{time_to_cvstime_rlog(identifier_from)}\"" if identifier_from
|
|
| 146 |
cmd << " #{shell_quote path_with_project}"
|
|
| 147 |
shellout(cmd) do |io| |
|
| 148 |
state="entry_start" |
|
| 149 |
|
|
| 150 |
commit_log=String.new |
|
| 151 |
revision=nil |
|
| 152 |
date=nil |
|
| 153 |
author=nil |
|
| 154 |
entry_path=nil |
|
| 155 |
entry_name=nil |
|
| 156 |
file_state=nil |
|
| 157 |
branch_map=nil |
|
| 158 |
|
|
| 159 |
path_with_project_utf8 = path_with_proj(path) |
|
| 160 |
path_with_project_locale = scm_iconv(@path_encoding, 'UTF-8', path_with_project_utf8) |
|
| 161 |
logger.debug "<cvs> revisions path:" + |
|
| 162 |
"'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
|
|
| 163 |
cmd_args = %w|-q rlog| |
|
| 164 |
cmd_args << "-d" << ">#{time_to_cvstime_rlog(identifier_from)}" if identifier_from
|
|
| 165 |
cmd_args << path_with_project_utf8 |
|
| 166 |
scm_cmd(*cmd_args) do |io| |
|
| 167 |
state = "entry_start" |
|
| 168 |
commit_log = String.new |
|
| 169 |
revision = nil |
|
| 170 |
date = nil |
|
| 171 |
author = nil |
|
| 172 |
entry_path = nil |
|
| 173 |
entry_name = nil |
|
| 174 |
file_state = nil |
|
| 175 |
branch_map = nil |
|
| 159 | 176 |
io.each_line() do |line| |
| 160 |
|
|
| 161 |
if state!="revision" && /^#{ENDLOG}/ =~ line
|
|
| 162 |
commit_log=String.new |
|
| 163 |
revision=nil |
|
| 164 |
state="entry_start" |
|
| 177 |
if state != "revision" && /^#{ENDLOG}/ =~ line
|
|
| 178 |
commit_log = String.new |
|
| 179 |
revision = nil |
|
| 180 |
state = "entry_start" |
|
| 165 | 181 |
end |
| 166 |
|
|
| 167 |
if state=="entry_start" |
|
| 168 |
branch_map=Hash.new |
|
| 169 |
if /^RCS file: #{Regexp.escape(root_url_path)}\/#{Regexp.escape(path_with_project)}(.+),v$/ =~ line
|
|
| 182 |
if state == "entry_start" |
|
| 183 |
branch_map = Hash.new |
|
| 184 |
if /^RCS file: #{Regexp.escape(root_url_path)}\/#{Regexp.escape(path_with_project_locale)}(.+),v$/ =~ line
|
|
| 170 | 185 |
entry_path = normalize_cvs_path($1) |
| 171 | 186 |
entry_name = normalize_path(File.basename($1)) |
| 172 | 187 |
logger.debug("Path #{entry_path} <=> Name #{entry_name}")
|
| 173 | 188 |
elsif /^head: (.+)$/ =~ line |
| 174 | 189 |
entry_headRev = $1 #unless entry.nil? |
| 175 | 190 |
elsif /^symbolic names:/ =~ line |
| 176 |
state="symbolic" #unless entry.nil?
|
|
| 191 |
state = "symbolic" #unless entry.nil?
|
|
| 177 | 192 |
elsif /^#{STARTLOG}/ =~ line
|
| 178 |
commit_log=String.new
|
|
| 179 |
state="revision"
|
|
| 180 |
end
|
|
| 193 |
commit_log = String.new
|
|
| 194 |
state = "revision"
|
|
| 195 |
end |
|
| 181 | 196 |
next |
| 182 |
elsif state=="symbolic"
|
|
| 183 |
if /^(.*):\s(.*)/ =~ (line.strip)
|
|
| 184 |
branch_map[$1]=$2
|
|
| 197 |
elsif state == "symbolic"
|
|
| 198 |
if /^(.*):\s(.*)/ =~ (line.strip) |
|
| 199 |
branch_map[$1] = $2
|
|
| 185 | 200 |
else |
| 186 |
state="tags"
|
|
| 201 |
state = "tags"
|
|
| 187 | 202 |
next |
| 188 |
end
|
|
| 189 |
elsif state=="tags"
|
|
| 203 |
end |
|
| 204 |
elsif state == "tags"
|
|
| 190 | 205 |
if /^#{STARTLOG}/ =~ line
|
| 191 | 206 |
commit_log = "" |
| 192 |
state="revision"
|
|
| 207 |
state = "revision"
|
|
| 193 | 208 |
elsif /^#{ENDLOG}/ =~ line
|
| 194 |
state="head"
|
|
| 209 |
state = "head"
|
|
| 195 | 210 |
end |
| 196 | 211 |
next |
| 197 |
elsif state=="revision"
|
|
| 212 |
elsif state == "revision"
|
|
| 198 | 213 |
if /^#{ENDLOG}/ =~ line || /^#{STARTLOG}/ =~ line
|
| 199 | 214 |
if revision |
| 200 |
|
|
| 201 |
revHelper=CvsRevisionHelper.new(revision) |
|
| 202 |
revBranch="HEAD" |
|
| 203 |
|
|
| 204 |
branch_map.each() do |branch_name,branch_point| |
|
| 215 |
revHelper = CvsRevisionHelper.new(revision) |
|
| 216 |
revBranch = "HEAD" |
|
| 217 |
branch_map.each() do |branch_name, branch_point| |
|
| 205 | 218 |
if revHelper.is_in_branch_with_symbol(branch_point) |
| 206 |
revBranch=branch_name
|
|
| 219 |
revBranch = branch_name
|
|
| 207 | 220 |
end |
| 208 | 221 |
end |
| 209 |
|
|
| 210 | 222 |
logger.debug("********** YIELD Revision #{revision}::#{revBranch}")
|
| 211 |
|
|
| 212 | 223 |
yield Revision.new({
|
| 213 |
:time => date, |
|
| 214 |
:author => author, |
|
| 215 |
:message=>commit_log.chomp,
|
|
| 224 |
:time => date,
|
|
| 225 |
:author => author,
|
|
| 226 |
:message => commit_log.chomp,
|
|
| 216 | 227 |
:paths => [{
|
| 217 | 228 |
:revision => revision, |
| 218 |
:branch=> revBranch, |
|
| 219 |
:path=>entry_path,
|
|
| 220 |
:name=>entry_name,
|
|
| 221 |
:kind=>'file',
|
|
| 222 |
:action=>file_state
|
|
| 223 |
}] |
|
| 224 |
}) |
|
| 229 |
:branch => revBranch,
|
|
| 230 |
:path => scm_iconv('UTF-8', @path_encoding, entry_path),
|
|
| 231 |
:name => scm_iconv('UTF-8', @path_encoding, entry_name),
|
|
| 232 |
:kind => 'file',
|
|
| 233 |
:action => file_state
|
|
| 234 |
}]
|
|
| 235 |
})
|
|
| 225 | 236 |
end |
| 226 |
|
|
| 227 |
commit_log=String.new |
|
| 228 |
revision=nil |
|
| 229 |
|
|
| 237 |
commit_log = String.new |
|
| 238 |
revision = nil |
|
| 230 | 239 |
if /^#{ENDLOG}/ =~ line
|
| 231 |
state="entry_start"
|
|
| 240 |
state = "entry_start"
|
|
| 232 | 241 |
end |
| 233 | 242 |
next |
| 234 | 243 |
end |
| 235 | 244 |
|
| 236 | 245 |
if /^branches: (.+)$/ =~ line |
| 237 |
#TODO: version.branch = $1 |
|
| 246 |
# TODO: version.branch = $1
|
|
| 238 | 247 |
elsif /^revision (\d+(?:\.\d+)+).*$/ =~ line |
| 239 |
revision = $1
|
|
| 248 |
revision = $1 |
|
| 240 | 249 |
elsif /^date:\s+(\d+.\d+.\d+\s+\d+:\d+:\d+)/ =~ line |
| 241 |
date = Time.parse($1) |
|
| 242 |
author = /author: ([^;]+)/.match(line)[1] |
|
| 243 |
file_state = /state: ([^;]+)/.match(line)[1] |
|
| 244 |
#TODO: linechanges only available in CVS.... maybe a feature our SVN implementation. i'm sure, they are |
|
| 245 |
# useful for stats or something else |
|
| 250 |
date = Time.parse($1) |
|
| 251 |
line_utf8 = scm_iconv('UTF-8', options[:log_encoding], line)
|
|
| 252 |
author_utf8 = /author: ([^;]+)/.match(line_utf8)[1] |
|
| 253 |
author = scm_iconv(options[:log_encoding], 'UTF-8', author_utf8) |
|
| 254 |
file_state = /state: ([^;]+)/.match(line)[1] |
|
| 255 |
# TODO: |
|
| 256 |
# linechanges only available in CVS.... |
|
| 257 |
# maybe a feature our SVN implementation. |
|
| 258 |
# I'm sure, they are useful for stats or something else |
|
| 246 | 259 |
# linechanges =/lines: \+(\d+) -(\d+)/.match(line) |
| 247 | 260 |
# unless linechanges.nil? |
| 248 | 261 |
# version.line_plus = linechanges[1] |
| ... | ... | |
| 257 | 270 |
end |
| 258 | 271 |
end |
| 259 | 272 |
end |
| 273 |
rescue ScmCommandAborted |
|
| 274 |
Revisions.new |
|
| 260 | 275 |
end |
| 261 | 276 |
|
| 262 | 277 |
def diff(path, identifier_from, identifier_to=nil) |
| 263 |
logger.debug "<cvs> diff path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
|
|
| 264 |
path_with_project="#{url}#{with_leading_slash(path)}"
|
|
| 265 |
cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rdiff -u -r#{identifier_to} -r#{identifier_from} #{shell_quote path_with_project}"
|
|
| 278 |
logger.debug "<cvs> diff path:'#{path}'" +
|
|
| 279 |
",identifier_from #{identifier_from}, identifier_to #{identifier_to}"
|
|
| 280 |
cmd_args = %w|rdiff -u| |
|
| 281 |
cmd_args << "-r#{identifier_to}"
|
|
| 282 |
cmd_args << "-r#{identifier_from}"
|
|
| 283 |
cmd_args << path_with_proj(path) |
|
| 266 | 284 |
diff = [] |
| 267 |
shellout(cmd) do |io|
|
|
| 285 |
scm_cmd(*cmd_args) do |io|
|
|
| 268 | 286 |
io.each_line do |line| |
| 269 | 287 |
diff << line |
| 270 | 288 |
end |
| 271 | 289 |
end |
| 272 |
return nil if $? && $?.exitstatus != 0 |
|
| 273 | 290 |
diff |
| 291 |
rescue ScmCommandAborted |
|
| 292 |
nil |
|
| 274 | 293 |
end |
| 275 | 294 |
|
| 276 | 295 |
def cat(path, identifier=nil) |
| 277 | 296 |
identifier = (identifier) ? identifier : "HEAD" |
| 278 | 297 |
logger.debug "<cvs> cat path:'#{path}',identifier #{identifier}"
|
| 279 |
path_with_project="#{url}#{with_leading_slash(path)}"
|
|
| 280 |
cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} co"
|
|
| 281 |
cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier
|
|
| 282 |
cmd << " -p #{shell_quote path_with_project}"
|
|
| 298 |
cmd_args = %w|-q co| |
|
| 299 |
cmd_args << "-D" << time_to_cvstime(identifier) if identifier |
|
| 300 |
cmd_args << "-p" << path_with_proj(path) |
|
| 283 | 301 |
cat = nil |
| 284 |
shellout(cmd) do |io|
|
|
| 302 |
scm_cmd(*cmd_args) do |io|
|
|
| 285 | 303 |
io.binmode |
| 286 | 304 |
cat = io.read |
| 287 | 305 |
end |
| 288 |
return nil if $? && $?.exitstatus != 0 |
|
| 289 | 306 |
cat |
| 307 |
rescue ScmCommandAborted |
|
| 308 |
nil |
|
| 290 | 309 |
end |
| 291 | 310 |
|
| 292 | 311 |
def annotate(path, identifier=nil) |
| 293 |
identifier = (identifier) ? identifier.to_i : "HEAD"
|
|
| 312 |
identifier = (identifier) ? identifier : "HEAD" |
|
| 294 | 313 |
logger.debug "<cvs> annotate path:'#{path}',identifier #{identifier}"
|
| 295 |
path_with_project="#{url}#{with_leading_slash(path)}"
|
|
| 296 |
cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rannotate -r#{identifier} #{shell_quote path_with_project}"
|
|
| 314 |
cmd_args = %w|rannotate| |
|
| 315 |
cmd_args << "-D" << time_to_cvstime(identifier) if identifier |
|
| 316 |
cmd_args << path_with_proj(path) |
|
| 297 | 317 |
blame = Annotate.new |
| 298 |
shellout(cmd) do |io|
|
|
| 318 |
scm_cmd(*cmd_args) do |io|
|
|
| 299 | 319 |
io.each_line do |line| |
| 300 | 320 |
next unless line =~ %r{^([\d\.]+)\s+\(([^\)]+)\s+[^\)]+\):\s(.*)$}
|
| 301 |
blame.add_line($3.rstrip, Revision.new(:revision => $1, :author => $2.strip)) |
|
| 321 |
blame.add_line( |
|
| 322 |
$3.rstrip, |
|
| 323 |
Revision.new( |
|
| 324 |
:revision => $1, |
|
| 325 |
:identifier => nil, |
|
| 326 |
:author => $2.strip |
|
| 327 |
)) |
|
| 302 | 328 |
end |
| 303 | 329 |
end |
| 304 |
return nil if $? && $?.exitstatus != 0 |
|
| 305 | 330 |
blame |
| 331 |
rescue ScmCommandAborted |
|
| 332 |
Annotate.new |
|
| 306 | 333 |
end |
| 307 | 334 |
|
| 308 | 335 |
private |
| ... | ... | |
| 317 | 344 |
# convert a date/time into the CVS-format |
| 318 | 345 |
def time_to_cvstime(time) |
| 319 | 346 |
return nil if time.nil? |
| 320 |
return Time.now if time == 'HEAD'
|
|
| 321 |
|
|
| 347 |
time = Time.now if time == 'HEAD'
|
|
| 348 |
|
|
| 322 | 349 |
unless time.kind_of? Time |
| 323 | 350 |
time = Time.parse(time) |
| 324 | 351 |
end |
| 325 |
return time.strftime("%Y-%m-%d %H:%M:%S")
|
|
| 352 |
return time_to_cvstime_rlog(time)
|
|
| 326 | 353 |
end |
| 327 | 354 |
|
| 328 | 355 |
def time_to_cvstime_rlog(time) |
| ... | ... | |
| 330 | 357 |
t1 = time.clone.localtime |
| 331 | 358 |
return t1.strftime("%Y-%m-%d %H:%M:%S")
|
| 332 | 359 |
end |
| 333 |
|
|
| 360 |
|
|
| 334 | 361 |
def normalize_cvs_path(path) |
| 335 | 362 |
normalize_path(path.gsub(/Attic\//,'')) |
| 336 | 363 |
end |
| 337 |
|
|
| 364 |
|
|
| 338 | 365 |
def normalize_path(path) |
| 339 | 366 |
path.sub(/^(\/)*(.*)/,'\2').sub(/(.*)(,v)+/,'\1') |
| 340 |
end |
|
| 341 |
end |
|
| 342 |
|
|
| 367 |
end |
|
| 368 |
|
|
| 369 |
def path_with_proj(path) |
|
| 370 |
"#{url}#{with_leading_slash(path)}"
|
|
| 371 |
end |
|
| 372 |
private :path_with_proj |
|
| 373 |
|
|
| 374 |
class Revision < Redmine::Scm::Adapters::Revision |
|
| 375 |
# Returns the readable identifier |
|
| 376 |
def format_identifier |
|
| 377 |
revision.to_s |
|
| 378 |
end |
|
| 379 |
end |
|
| 380 |
|
|
| 381 |
def scm_cmd(*args, &block) |
|
| 382 |
full_args = [CVS_BIN, '-d', root_url] |
|
| 383 |
full_args += args |
|
| 384 |
full_args_locale = [] |
|
| 385 |
full_args.map do |e| |
|
| 386 |
full_args_locale << scm_iconv(@path_encoding, 'UTF-8', e) |
|
| 387 |
end |
|
| 388 |
ret = shellout(full_args_locale.map { |e| shell_quote e.to_s }.join(' '), &block)
|
|
| 389 |
if $? && $?.exitstatus != 0 |
|
| 390 |
raise ScmCommandAborted, "cvs exited with non-zero status: #{$?.exitstatus}"
|
|
| 391 |
end |
|
| 392 |
ret |
|
| 393 |
end |
|
| 394 |
private :scm_cmd |
|
| 395 |
end |
|
| 396 |
|
|
| 343 | 397 |
class CvsRevisionHelper |
| 344 | 398 |
attr_accessor :complete_rev, :revision, :base, :branchid |
| 345 |
|
|
| 399 |
|
|
| 346 | 400 |
def initialize(complete_rev) |
| 347 | 401 |
@complete_rev = complete_rev |
| 348 | 402 |
parseRevision() |
| 349 | 403 |
end |
| 350 |
|
|
| 404 |
|
|
| 351 | 405 |
def branchPoint |
| 352 | 406 |
return @base |
| 353 | 407 |
end |
| 354 |
|
|
| 408 |
|
|
| 355 | 409 |
def branchVersion |
| 356 | 410 |
if isBranchRevision |
| 357 | 411 |
return @base+"."+@branchid |
| 358 | 412 |
end |
| 359 | 413 |
return @base |
| 360 | 414 |
end |
| 361 |
|
|
| 415 |
|
|
| 362 | 416 |
def isBranchRevision |
| 363 | 417 |
!@branchid.nil? |
| 364 | 418 |
end |
| 365 |
|
|
| 419 |
|
|
| 366 | 420 |
def prevRev |
| 367 |
unless @revision==0
|
|
| 368 |
return buildRevision(@revision-1)
|
|
| 421 |
unless @revision == 0
|
|
| 422 |
return buildRevision( @revision - 1 )
|
|
| 369 | 423 |
end |
| 370 |
return buildRevision(@revision)
|
|
| 424 |
return buildRevision( @revision )
|
|
| 371 | 425 |
end |
| 372 |
|
|
| 426 |
|
|
| 373 | 427 |
def is_in_branch_with_symbol(branch_symbol) |
| 374 |
bpieces=branch_symbol.split(".")
|
|
| 375 |
branch_start="#{bpieces[0..-3].join(".")}.#{bpieces[-1]}"
|
|
| 376 |
return (branchVersion==branch_start)
|
|
| 428 |
bpieces = branch_symbol.split(".")
|
|
| 429 |
branch_start = "#{bpieces[0..-3].join(".")}.#{bpieces[-1]}"
|
|
| 430 |
return ( branchVersion == branch_start )
|
|
| 377 | 431 |
end |
| 378 |
|
|
| 432 |
|
|
| 379 | 433 |
private |
| 380 | 434 |
def buildRevision(rev) |
| 381 |
if rev== 0 |
|
| 435 |
if rev == 0
|
|
| 382 | 436 |
if @branchid.nil? |
| 383 |
@base+".0"
|
|
| 437 |
@base + ".0"
|
|
| 384 | 438 |
else |
| 385 | 439 |
@base |
| 386 | 440 |
end |
| 387 |
elsif @branchid.nil?
|
|
| 388 |
@base+"."+rev.to_s
|
|
| 441 |
elsif @branchid.nil? |
|
| 442 |
@base + "." + rev.to_s
|
|
| 389 | 443 |
else |
| 390 |
@base+"."+@branchid+"."+rev.to_s
|
|
| 444 |
@base + "." + @branchid + "." + rev.to_s
|
|
| 391 | 445 |
end |
| 392 | 446 |
end |
| 393 |
|
|
| 447 |
|
|
| 394 | 448 |
# Interpretiert die cvs revisionsnummern wie z.b. 1.14 oder 1.3.0.15 |
| 395 | 449 |
def parseRevision() |
| 396 |
pieces=@complete_rev.split(".")
|
|
| 397 |
@revision=pieces.last.to_i
|
|
| 398 |
baseSize=1
|
|
| 399 |
baseSize+=(pieces.size/2)
|
|
| 400 |
@base=pieces[0..-baseSize].join(".")
|
|
| 450 |
pieces = @complete_rev.split(".")
|
|
| 451 |
@revision = pieces.last.to_i
|
|
| 452 |
baseSize = 1
|
|
| 453 |
baseSize += (pieces.size / 2)
|
|
| 454 |
@base = pieces[0..-baseSize].join(".")
|
|
| 401 | 455 |
if baseSize > 2 |
| 402 |
@branchid=pieces[-2]
|
|
| 403 |
end
|
|
| 456 |
@branchid = pieces[-2]
|
|
| 457 |
end |
|
| 404 | 458 |
end |
| 405 | 459 |
end |
| 406 | 460 |
end |
| lib/redmine/scm/adapters/.svn/text-base/darcs_adapter.rb.svn-base | ||
|---|---|---|
| 1 |
# redMine - project management software
|
|
| 2 |
# Copyright (C) 2006-2007 Jean-Philippe Lang
|
|
| 1 |
# Redmine - project management software
|
|
| 2 |
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| 6 | 6 |
# as published by the Free Software Foundation; either version 2 |
| 7 | 7 |
# of the License, or (at your option) any later version. |
| 8 |
#
|
|
| 8 |
# |
|
| 9 | 9 |
# This program is distributed in the hope that it will be useful, |
| 10 | 10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | 11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | 12 |
# GNU General Public License for more details. |
| 13 |
#
|
|
| 13 |
# |
|
| 14 | 14 |
# You should have received a copy of the GNU General Public License |
| 15 | 15 |
# along with this program; if not, write to the Free Software |
| 16 | 16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| ... | ... | |
| 76 | 76 |
|
| 77 | 77 |
# Returns an Entries collection |
| 78 | 78 |
# or nil if the given path doesn't exist in the repository |
| 79 |
def entries(path=nil, identifier=nil) |
|
| 79 |
def entries(path=nil, identifier=nil, options={})
|
|
| 80 | 80 |
path_prefix = (path.blank? ? '' : "#{path}/")
|
| 81 | 81 |
if path.blank? |
| 82 | 82 |
path = ( self.class.client_version_above?([2, 2, 0]) ? @url : '.' ) |
| lib/redmine/scm/adapters/.svn/text-base/filesystem_adapter.rb.svn-base | ||
|---|---|---|
| 1 |
# redMine - project management software
|
|
| 2 |
# Copyright (C) 2006-2007 Jean-Philippe Lang
|
|
| 1 |
# RedMine - project management software
|
|
| 2 |
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# FileSystem adapter |
| 5 | 5 |
# File written by Paul Rivier, at Demotera. |
| ... | ... | |
| 8 | 8 |
# modify it under the terms of the GNU General Public License |
| 9 | 9 |
# as published by the Free Software Foundation; either version 2 |
| 10 | 10 |
# of the License, or (at your option) any later version. |
| 11 |
#
|
|
| 11 |
# |
|
| 12 | 12 |
# This program is distributed in the hope that it will be useful, |
| 13 | 13 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | 14 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | 15 |
# GNU General Public License for more details. |
| 16 |
#
|
|
| 16 |
# |
|
| 17 | 17 |
# You should have received a copy of the GNU General Public License |
| 18 | 18 |
# along with this program; if not, write to the Free Software |
| 19 | 19 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| ... | ... | |
| 35 | 35 |
def initialize(url, root_url=nil, login=nil, password=nil, |
| 36 | 36 |
path_encoding=nil) |
| 37 | 37 |
@url = with_trailling_slash(url) |
| 38 |
@path_encoding = path_encoding || 'UTF-8' |
|
| 38 |
@path_encoding = path_encoding.blank? ? 'UTF-8' : path_encoding |
|
| 39 |
end |
|
| 40 |
|
|
| 41 |
def path_encoding |
|
| 42 |
@path_encoding |
|
| 39 | 43 |
end |
| 40 | 44 |
|
| 41 | 45 |
def format_path_ends(path, leading=true, trailling=true) |
| 42 |
path = leading ? with_leading_slash(path) :
|
|
| 46 |
path = leading ? with_leading_slash(path) : |
|
| 43 | 47 |
without_leading_slash(path) |
| 44 |
trailling ? with_trailling_slash(path) :
|
|
| 45 |
without_trailling_slash(path)
|
|
| 48 |
trailling ? with_trailling_slash(path) : |
|
| 49 |
without_trailling_slash(path) |
|
| 46 | 50 |
end |
| 47 | 51 |
|
| 48 | 52 |
def info |
| ... | ... | |
| 54 | 58 |
return nil |
| 55 | 59 |
end |
| 56 | 60 |
|
| 57 |
def entries(path="", identifier=nil) |
|
| 61 |
def entries(path="", identifier=nil, options={})
|
|
| 58 | 62 |
entries = Entries.new |
| 59 | 63 |
trgt_utf8 = target(path) |
| 60 | 64 |
trgt = scm_iconv(@path_encoding, 'UTF-8', trgt_utf8) |
| 61 | 65 |
Dir.new(trgt).each do |e1| |
| 62 | 66 |
e_utf8 = scm_iconv('UTF-8', @path_encoding, e1)
|
| 63 |
relative_path_utf8 = format_path_ends((format_path_ends(path,false,true) + e_utf8),false,false) |
|
| 67 |
next if e_utf8.blank? |
|
| 68 |
relative_path_utf8 = format_path_ends( |
|
| 69 |
(format_path_ends(path,false,true) + e_utf8),false,false) |
|
| 64 | 70 |
t1_utf8 = target(relative_path_utf8) |
| 65 | 71 |
t1 = scm_iconv(@path_encoding, 'UTF-8', t1_utf8) |
| 66 | 72 |
relative_path = scm_iconv(@path_encoding, 'UTF-8', relative_path_utf8) |
| ... | ... | |
| 76 | 82 |
:path => utf_8_path, |
| 77 | 83 |
:kind => (File.directory?(t1) ? 'dir' : 'file'), |
| 78 | 84 |
:size => (File.directory?(t1) ? nil : [File.size(t1)].pack('l').unpack('L').first),
|
| 79 |
:lastrev =>
|
|
| 85 |
:lastrev => |
|
| 80 | 86 |
Revision.new({:time => (File.mtime(t1)) })
|
| 81 | 87 |
}) |
| 82 | 88 |
end |
| lib/redmine/scm/adapters/.svn/text-base/git_adapter.rb.svn-base | ||
|---|---|---|
| 1 |
# redMine - project management software
|
|
| 2 |
# Copyright (C) 2006-2007 Jean-Philippe Lang
|
|
| 1 |
# Redmine - project management software
|
|
| 2 |
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| 6 | 6 |
# as published by the Free Software Foundation; either version 2 |
| 7 | 7 |
# of the License, or (at your option) any later version. |
| 8 |
#
|
|
| 8 |
# |
|
| 9 | 9 |
# This program is distributed in the hope that it will be useful, |
| 10 | 10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | 11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | 12 |
# GNU General Public License for more details. |
| 13 |
#
|
|
| 13 |
# |
|
| 14 | 14 |
# You should have received a copy of the GNU General Public License |
| 15 | 15 |
# along with this program; if not, write to the Free Software |
| 16 | 16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| ... | ... | |
| 22 | 22 |
module Adapters |
| 23 | 23 |
class GitAdapter < AbstractAdapter |
| 24 | 24 |
|
| 25 |
SCM_GIT_REPORT_LAST_COMMIT = true |
|
| 26 |
|
|
| 27 | 25 |
# Git executable name |
| 28 | 26 |
GIT_BIN = Redmine::Configuration['scm_git_command'] || "git" |
| 29 | 27 |
|
| ... | ... | |
| 64 | 62 |
|
| 65 | 63 |
def initialize(url, root_url=nil, login=nil, password=nil, path_encoding=nil) |
| 66 | 64 |
super |
| 67 |
@flag_report_last_commit = SCM_GIT_REPORT_LAST_COMMIT |
|
| 65 |
@path_encoding = path_encoding.blank? ? 'UTF-8' : path_encoding |
|
| 66 |
end |
|
| 67 |
|
|
| 68 |
def path_encoding |
|
| 69 |
@path_encoding |
|
| 68 | 70 |
end |
| 69 | 71 |
|
| 70 | 72 |
def info |
| ... | ... | |
| 78 | 80 |
def branches |
| 79 | 81 |
return @branches if @branches |
| 80 | 82 |
@branches = [] |
| 81 |
cmd = "#{self.class.sq_bin} --git-dir #{target('')} branch --no-color"
|
|
| 82 |
shellout(cmd) do |io|
|
|
| 83 |
cmd_args = %w|branch --no-color|
|
|
| 84 |
scm_cmd(*cmd_args) do |io|
|
|
| 83 | 85 |
io.each_line do |line| |
| 84 | 86 |
@branches << line.match('\s*\*?\s*(.*)$')[1]
|
| 85 | 87 |
end |
| 86 | 88 |
end |
| 87 | 89 |
@branches.sort! |
| 90 |
rescue ScmCommandAborted |
|
| 91 |
nil |
|
| 88 | 92 |
end |
| 89 | 93 |
|
| 90 | 94 |
def tags |
| 91 | 95 |
return @tags if @tags |
| 92 |
cmd = "#{self.class.sq_bin} --git-dir #{target('')} tag"
|
|
| 93 |
shellout(cmd) do |io|
|
|
| 96 |
cmd_args = %w|tag|
|
|
| 97 |
scm_cmd(*cmd_args) do |io|
|
|
| 94 | 98 |
@tags = io.readlines.sort!.map{|t| t.strip}
|
| 95 | 99 |
end |
| 100 |
rescue ScmCommandAborted |
|
| 101 |
nil |
|
| 102 |
end |
|
| 103 |
|
|
| 104 |
def default_branch |
|
| 105 |
bras = self.branches |
|
| 106 |
return nil if bras.nil? |
|
| 107 |
bras.include?('master') ? 'master' : bras.first
|
|
| 108 |
end |
|
| 109 |
|
|
| 110 |
def entry(path=nil, identifier=nil) |
|
| 111 |
parts = path.to_s.split(%r{[\/\\]}).select {|n| !n.blank?}
|
|
| 112 |
search_path = parts[0..-2].join('/')
|
|
| 113 |
search_name = parts[-1] |
|
| 114 |
if search_path.blank? && search_name.blank? |
|
| 115 |
# Root entry |
|
| 116 |
Entry.new(:path => '', :kind => 'dir') |
|
| 117 |
else |
|
| 118 |
# Search for the entry in the parent directory |
|
| 119 |
es = entries(search_path, identifier, |
|
| 120 |
options = {:report_last_commit => false})
|
|
| 121 |
es ? es.detect {|e| e.name == search_name} : nil
|
|
| 122 |
end |
|
| 96 | 123 |
end |
| 97 | 124 |
|
| 98 |
def default_branch |
|
| 99 |
branches.include?('master') ? 'master' : branches.first
|
|
| 100 |
end |
|
| 101 |
|
|
| 102 |
def entries(path=nil, identifier=nil) |
|
| 125 |
def entries(path=nil, identifier=nil, options={})
|
|
| 103 | 126 |
path ||= '' |
| 127 |
p = scm_iconv(@path_encoding, 'UTF-8', path) |
|
| 104 | 128 |
entries = Entries.new |
| 105 |
cmd = "#{self.class.sq_bin} --git-dir #{target('')} ls-tree -l "
|
|
| 106 |
cmd << shell_quote("HEAD:" + path) if identifier.nil?
|
|
| 107 |
cmd << shell_quote(identifier + ":" + path) if identifier
|
|
| 108 |
shellout(cmd) do |io|
|
|
| 129 |
cmd_args = %w|ls-tree -l|
|
|
| 130 |
cmd_args << "HEAD:#{p}" if identifier.nil?
|
|
| 131 |
cmd_args << "#{identifier}:#{p}" if identifier
|
|
| 132 |
scm_cmd(*cmd_args) do |io|
|
|
| 109 | 133 |
io.each_line do |line| |
| 110 | 134 |
e = line.chomp.to_s |
| 111 | 135 |
if e =~ /^\d+\s+(\w+)\s+([0-9a-f]{40})\s+([0-9-]+)\t(.+)$/
|
| 112 | 136 |
type = $1 |
| 113 |
sha = $2 |
|
| 137 |
sha = $2
|
|
| 114 | 138 |
size = $3 |
| 115 | 139 |
name = $4 |
| 116 |
full_path = path.empty? ? name : "#{path}/#{name}"
|
|
| 117 |
entries << Entry.new({:name => name,
|
|
| 118 |
:path => full_path, |
|
| 140 |
if name.respond_to?(:force_encoding) |
|
| 141 |
name.force_encoding(@path_encoding) |
|
| 142 |
end |
|
| 143 |
full_path = p.empty? ? name : "#{p}/#{name}"
|
|
| 144 |
n = scm_iconv('UTF-8', @path_encoding, name)
|
|
| 145 |
full_p = scm_iconv('UTF-8', @path_encoding, full_path)
|
|
| 146 |
entries << Entry.new({:name => n,
|
|
| 147 |
:path => full_p, |
|
| 119 | 148 |
:kind => (type == "tree") ? 'dir' : 'file', |
| 120 | 149 |
:size => (type == "tree") ? nil : size, |
| 121 |
:lastrev => @flag_report_last_commit ? lastrev(full_path,identifier) : Revision.new |
|
| 150 |
:lastrev => options[:report_last_commit] ? |
|
| 151 |
lastrev(full_path, identifier) : Revision.new |
|
| 122 | 152 |
}) unless entries.detect{|entry| entry.name == name}
|
| 123 | 153 |
end |
| 124 | 154 |
end |
| 125 | 155 |
end |
| 126 |
return nil if $? && $?.exitstatus != 0 |
|
| 127 | 156 |
entries.sort_by_name |
| 157 |
rescue ScmCommandAborted |
|
| 158 |
nil |
|
| 128 | 159 |
end |
| 129 | 160 |
|
| 130 | 161 |
def lastrev(path, rev) |
| 131 | 162 |
return nil if path.nil? |
| 132 | 163 |
cmd_args = %w|log --no-color --encoding=UTF-8 --date=iso --pretty=fuller --no-merges -n 1| |
| 133 |
cmd_args << rev if rev
|
|
| 164 |
cmd_args << rev if rev |
|
| 134 | 165 |
cmd_args << "--" << path unless path.empty? |
| 135 | 166 |
lines = [] |
| 136 | 167 |
scm_cmd(*cmd_args) { |io| lines = io.readlines }
|
| ... | ... | |
| 141 | 172 |
|
| 142 | 173 |
Revision.new({
|
| 143 | 174 |
:identifier => id, |
| 144 |
:scmid => id, |
|
| 145 |
:author => author,
|
|
| 146 |
:time => time, |
|
| 147 |
:message => nil,
|
|
| 148 |
:paths => nil
|
|
| 175 |
:scmid => id,
|
|
| 176 |
:author => author,
|
|
| 177 |
:time => time,
|
|
| 178 |
:message => nil,
|
|
| 179 |
:paths => nil
|
|
| 149 | 180 |
}) |
| 150 | 181 |
rescue NoMethodError => e |
| 151 | 182 |
logger.error("The revision '#{path}' has a wrong format")
|
| ... | ... | |
| 156 | 187 |
end |
| 157 | 188 |
|
| 158 | 189 |
def revisions(path, identifier_from, identifier_to, options={})
|
| 159 |
revisions = Revisions.new
|
|
| 190 |
revs = Revisions.new |
|
| 160 | 191 |
cmd_args = %w|log --no-color --encoding=UTF-8 --raw --date=iso --pretty=fuller| |
| 161 | 192 |
cmd_args << "--reverse" if options[:reverse] |
| 162 | 193 |
cmd_args << "--all" if options[:all] |
| ... | ... | |
| 166 | 197 |
from_to << "#{identifier_to}" if identifier_to
|
| 167 | 198 |
cmd_args << from_to if !from_to.empty? |
| 168 | 199 |
cmd_args << "--since=#{options[:since].strftime("%Y-%m-%d %H:%M:%S")}" if options[:since]
|
| 169 |
cmd_args << "--" << path if path && !path.empty?
|
|
| 200 |
cmd_args << "--" << scm_iconv(@path_encoding, 'UTF-8', path) if path && !path.empty?
|
|
| 170 | 201 |
|
| 171 | 202 |
scm_cmd *cmd_args do |io| |
| 172 | 203 |
files=[] |
| 173 | 204 |
changeset = {}
|
| 174 | 205 |
parsing_descr = 0 #0: not parsing desc or files, 1: parsing desc, 2: parsing files |
| 175 |
revno = 1 |
|
| 176 | 206 |
|
| 177 | 207 |
io.each_line do |line| |
| 178 | 208 |
if line =~ /^commit ([0-9a-f]{40})$/
|
| ... | ... | |
| 182 | 212 |
parsing_descr = 0 |
| 183 | 213 |
revision = Revision.new({
|
| 184 | 214 |
:identifier => changeset[:commit], |
| 185 |
:scmid => changeset[:commit], |
|
| 186 |
:author => changeset[:author], |
|
| 187 |
:time => Time.parse(changeset[:date]), |
|
| 188 |
:message => changeset[:description], |
|
| 189 |
:paths => files |
|
| 215 |
:scmid => changeset[:commit],
|
|
| 216 |
:author => changeset[:author],
|
|
| 217 |
:time => Time.parse(changeset[:date]),
|
|
| 218 |
:message => changeset[:description],
|
|
| 219 |
:paths => files
|
|
| 190 | 220 |
}) |
| 191 | 221 |
if block_given? |
| 192 | 222 |
yield revision |
| 193 | 223 |
else |
| 194 |
revisions << revision
|
|
| 224 |
revs << revision |
|
| 195 | 225 |
end |
| 196 | 226 |
changeset = {}
|
| 197 | 227 |
files = [] |
| 198 |
revno = revno + 1 |
|
| 199 | 228 |
end |
| 200 | 229 |
changeset[:commit] = $1 |
| 201 | 230 |
elsif (parsing_descr == 0) && line =~ /^(\w+):\s*(.*)$/ |
| ... | ... | |
| 210 | 239 |
parsing_descr = 1 |
| 211 | 240 |
changeset[:description] = "" |
| 212 | 241 |
elsif (parsing_descr == 1 || parsing_descr == 2) \ |
| 213 |
&& line =~ /^:\d+\s+\d+\s+[0-9a-f.]+\s+[0-9a-f.]+\s+(\w)\t(.+)$/ |
|
| 242 |
&& line =~ /^:\d+\s+\d+\s+[0-9a-f.]+\s+[0-9a-f.]+\s+(\w)\t(.+)$/
|
|
| 214 | 243 |
parsing_descr = 2 |
| 215 |
fileaction = $1 |
|
| 216 |
filepath = $2 |
|
| 217 |
files << {:action => fileaction, :path => filepath}
|
|
| 244 |
fileaction = $1 |
|
| 245 |
filepath = $2 |
|
| 246 |
p = scm_iconv('UTF-8', @path_encoding, filepath)
|
|
| 247 |
files << {:action => fileaction, :path => p}
|
|
| 218 | 248 |
elsif (parsing_descr == 1 || parsing_descr == 2) \ |
| 219 |
&& line =~ /^:\d+\s+\d+\s+[0-9a-f.]+\s+[0-9a-f.]+\s+(\w)\d+\s+(\S+)\t(.+)$/ |
|
| 249 |
&& line =~ /^:\d+\s+\d+\s+[0-9a-f.]+\s+[0-9a-f.]+\s+(\w)\d+\s+(\S+)\t(.+)$/
|
|
| 220 | 250 |
parsing_descr = 2 |
| 221 |
fileaction = $1 |
|
| 222 |
filepath = $3 |
|
| 223 |
files << {:action => fileaction, :path => filepath}
|
|
| 251 |
fileaction = $1 |
|
| 252 |
filepath = $3 |
|
| 253 |
p = scm_iconv('UTF-8', @path_encoding, filepath)
|
|
| 254 |
files << {:action => fileaction, :path => p}
|
|
| 224 | 255 |
elsif (parsing_descr == 1) && line.chomp.to_s == "" |
| 225 | 256 |
parsing_descr = 2 |
| 226 | 257 |
elsif (parsing_descr == 1) |
| 227 | 258 |
changeset[:description] << line[4..-1] |
| 228 | 259 |
end |
| 229 |
end
|
|
| 260 |
end |
|
| 230 | 261 |
|
| 231 | 262 |
if changeset[:commit] |
| 232 | 263 |
revision = Revision.new({
|
| 233 | 264 |
:identifier => changeset[:commit], |
| 234 |
:scmid => changeset[:commit], |
|
| 235 |
:author => changeset[:author], |
|
| 236 |
:time => Time.parse(changeset[:date]), |
|
| 237 |
:message => changeset[:description], |
|
| 238 |
:paths => files |
|
| 239 |
}) |
|
| 240 |
|
|
| 265 |
:scmid => changeset[:commit], |
|
| 266 |
:author => changeset[:author], |
|
| 267 |
:time => Time.parse(changeset[:date]), |
|
| 268 |
:message => changeset[:description], |
|
| 269 |
:paths => files |
|
| 270 |
}) |
|
| 241 | 271 |
if block_given? |
| 242 | 272 |
yield revision |
| 243 | 273 |
else |
| 244 |
revisions << revision
|
|
| 274 |
revs << revision |
|
| 245 | 275 |
end |
| 246 | 276 |
end |
| 247 | 277 |
end |
| 248 |
revisions |
|
| 249 |
rescue ScmCommandAborted |
|
| 250 |
revisions |
|
| 278 |
revs |
|
| 279 |
rescue ScmCommandAborted => e |
|
| 280 |
logger.error("git log #{from_to.to_s} error: #{e.message}")
|
|
| 281 |
revs |
|
| 251 | 282 |
end |
| 252 | 283 |
|
| 253 | 284 |
def diff(path, identifier_from, identifier_to=nil) |
| 254 | 285 |
path ||= '' |
| 255 |
|
|
| 286 |
cmd_args = [] |
|
| 256 | 287 |
if identifier_to |
| 257 |
cmd = "#{self.class.sq_bin} --git-dir #{target('')} diff --no-color #{shell_quote identifier_to} #{shell_quote identifier_from}"
|
|
| 288 |
cmd_args << "diff" << "--no-color" << identifier_to << identifier_from
|
|
| 258 | 289 |
else |
| 259 |
cmd = "#{self.class.sq_bin} --git-dir #{target('')} show --no-color #{shell_quote identifier_from}"
|
|
| 290 |
cmd_args << "show" << "--no-color" << identifier_from
|
|
| 260 | 291 |
end |
| 261 |
|
|
| 262 |
cmd << " -- #{shell_quote path}" unless path.empty?
|
|
| 292 |
cmd_args << "--" << scm_iconv(@path_encoding, 'UTF-8', path) unless path.empty? |
|
| 263 | 293 |
diff = [] |
| 264 |
shellout(cmd) do |io|
|
|
| 294 |
scm_cmd *cmd_args do |io|
|
|
| 265 | 295 |
io.each_line do |line| |
| 266 | 296 |
diff << line |
| 267 | 297 |
end |
| 268 | 298 |
end |
| 269 |
return nil if $? && $?.exitstatus != 0 |
|
| 270 | 299 |
diff |
| 300 |
rescue ScmCommandAborted |
|
| 301 |
nil |
|
| 271 | 302 |
end |
| 272 |
|
|
| 303 |
|
|
| 273 | 304 |
def annotate(path, identifier=nil) |
| 274 | 305 |
identifier = 'HEAD' if identifier.blank? |
| 275 |
cmd = "#{self.class.sq_bin} --git-dir #{target('')} blame -p #{shell_quote identifier} -- #{shell_quote path}"
|
|
| 306 |
cmd_args = %w|blame| |
|
| 307 |
cmd_args << "-p" << identifier << "--" << scm_iconv(@path_encoding, 'UTF-8', path) |
|
| 276 | 308 |
blame = Annotate.new |
| 277 | 309 |
content = nil |
| 278 |
shellout(cmd) { |io| io.binmode; content = io.read }
|
|
| 279 |
return nil if $? && $?.exitstatus != 0 |
|
| 310 |
scm_cmd(*cmd_args) { |io| io.binmode; content = io.read }
|
|
| 280 | 311 |
# git annotates binary files |
| 281 | 312 |
return nil if content.is_binary_data? |
| 282 | 313 |
identifier = '' |
| ... | ... | |
| 288 | 319 |
elsif line =~ /^author (.+)/ |
| 289 | 320 |
authors_by_commit[identifier] = $1.strip |
| 290 | 321 |
elsif line =~ /^\t(.*)/ |
| 291 |
blame.add_line($1, Revision.new(:identifier => identifier, :author => authors_by_commit[identifier])) |
|
| 322 |
blame.add_line($1, Revision.new( |
|
| 323 |
:identifier => identifier, |
|
| 324 |
:revision => identifier, |
|
| 325 |
:scmid => identifier, |
|
| 326 |
:author => authors_by_commit[identifier] |
|
| 327 |
)) |
|
| 292 | 328 |
identifier = '' |
| 293 | 329 |
author = '' |
| 294 | 330 |
end |
| 295 | 331 |
end |
| 296 | 332 |
blame |
| 333 |
rescue ScmCommandAborted |
|
| 334 |
nil |
|
| 297 | 335 |
end |
| 298 | 336 |
|
| 299 | 337 |
def cat(path, identifier=nil) |
| 300 | 338 |
if identifier.nil? |
| 301 | 339 |
identifier = 'HEAD' |
| 302 | 340 |
end |
| 303 |
cmd = "#{self.class.sq_bin} --git-dir #{target('')} show --no-color #{shell_quote(identifier + ':' + path)}"
|
|
| 341 |
cmd_args = %w|show --no-color| |
|
| 342 |
cmd_args << "#{identifier}:#{scm_iconv(@path_encoding, 'UTF-8', path)}"
|
|
| 304 | 343 |
cat = nil |
| 305 |
shellout(cmd) do |io|
|
|
| 344 |
scm_cmd(*cmd_args) do |io|
|
|
| 306 | 345 |
io.binmode |
| 307 | 346 |
cat = io.read |
| 308 | 347 |
end |
| 309 |
return nil if $? && $?.exitstatus != 0 |
|
| 310 | 348 |
cat |
| 349 |
rescue ScmCommandAborted |
|
| 350 |
nil |
|
| 311 | 351 |
end |
| 312 | 352 |
|
| 313 | 353 |
class Revision < Redmine::Scm::Adapters::Revision |
| ... | ... | |
| 320 | 360 |
def scm_cmd(*args, &block) |
| 321 | 361 |
repo_path = root_url || url |
| 322 | 362 |
full_args = [GIT_BIN, '--git-dir', repo_path] |
| 363 |
if self.class.client_version_above?([1, 7, 2]) |
|
| 364 |
full_args << '-c' << 'core.quotepath=false' |
|
| 365 |
full_args << '-c' << 'log.decorate=no' |
|
| 366 |
end |
|
| 323 | 367 |
full_args += args |
| 324 | 368 |
ret = shellout(full_args.map { |e| shell_quote e.to_s }.join(' '), &block)
|
| 325 | 369 |
if $? && $?.exitstatus != 0 |
| lib/redmine/scm/adapters/.svn/text-base/mercurial_adapter.rb.svn-base | ||
|---|---|---|
| 1 |
# redMine - project management software
|
|
| 2 |
# Copyright (C) 2006-2007 Jean-Philippe Lang
|
|
| 1 |
# Redmine - project management software
|
|
| 2 |
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| 6 | 6 |
# as published by the Free Software Foundation; either version 2 |
| 7 | 7 |
# of the License, or (at your option) any later version. |
| 8 |
#
|
|
| 8 |
# |
|
| 9 | 9 |
# This program is distributed in the hope that it will be useful, |
| 10 | 10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | 11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | 12 |
# GNU General Public License for more details. |
| 13 |
#
|
|
| 13 |
# |
|
| 14 | 14 |
# You should have received a copy of the GNU General Public License |
| 15 | 15 |
# along with this program; if not, write to the Free Software |
| 16 | 16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| ... | ... | |
| 47 | 47 |
end |
| 48 | 48 |
|
| 49 | 49 |
def client_available |
| 50 |
!client_version.empty?
|
|
| 50 |
client_version_above?([0, 9, 5])
|
|
| 51 | 51 |
end |
| 52 | 52 |
|
| 53 | 53 |
def hgversion |
| ... | ... | |
| 83 | 83 |
|
| 84 | 84 |
def initialize(url, root_url=nil, login=nil, password=nil, path_encoding=nil) |
| 85 | 85 |
super |
| 86 |
@path_encoding = path_encoding || 'UTF-8' |
|
| 86 |
@path_encoding = path_encoding.blank? ? 'UTF-8' : path_encoding |
|
| 87 |
end |
|
| 88 |
|
|
| 89 |
def path_encoding |
|
| 90 |
@path_encoding |
|
| 87 | 91 |
end |
| 88 | 92 |
|
| 89 | 93 |
def info |
| ... | ... | |
| 118 | 122 |
end |
| 119 | 123 |
|
| 120 | 124 |
def summary |
| 121 |
return @summary if @summary
|
|
| 125 |
return @summary if @summary |
|
| 122 | 126 |
hg 'rhsummary' do |io| |
| 123 | 127 |
output = io.read |
| 124 | 128 |
if output.respond_to?(:force_encoding) |
| ... | ... | |
| 132 | 136 |
end |
| 133 | 137 |
private :summary |
| 134 | 138 |
|
| 135 |
def entries(path=nil, identifier=nil) |
|
| 139 |
def entries(path=nil, identifier=nil, options={})
|
|
| 136 | 140 |
p1 = scm_iconv(@path_encoding, 'UTF-8', path) |
| 137 | 141 |
manifest = hg('rhmanifest', '-r', CGI.escape(hgrev(identifier)),
|
| 138 | 142 |
CGI.escape(without_leading_slash(p1.to_s))) do |io| |
| ... | ... | |
| 193 | 197 |
rescue |
| 194 | 198 |
end |
| 195 | 199 |
end |
| 196 |
|
|
| 197 | 200 |
as_ary(log['logentry']).each do |le| |
| 198 | 201 |
cpalist = as_ary(le['paths']['path-copied']).map do |e| |
| 199 |
[e['__content__'], e['copyfrom-path']].map { |s| CGI.unescape(s) }
|
|
| 202 |
[e['__content__'], e['copyfrom-path']].map do |s| |
|
| 203 |
scm_iconv('UTF-8', @path_encoding, CGI.unescape(s))
|
|
| 204 |
end |
|
| 200 | 205 |
end |
| 201 | 206 |
cpmap = Hash[*cpalist.flatten] |
| 202 |
|
|
| 203 | 207 |
paths = as_ary(le['paths']['path']).map do |e| |
| 204 | 208 |
p = scm_iconv('UTF-8', @path_encoding, CGI.unescape(e['__content__']) )
|
| 205 |
{:action => e['action'], :path => with_leading_slash(p),
|
|
| 206 |
:from_path => (cpmap.member?(p) ? with_leading_slash(cpmap[p]) : nil), |
|
| 207 |
:from_revision => (cpmap.member?(p) ? le['revision'] : nil)} |
|
| 209 |
{:action => e['action'],
|
|
| 210 |
:path => with_leading_slash(p), |
|
| 211 |
:from_path => (cpmap.member?(p) ? with_leading_slash(cpmap[p]) : nil), |
|
| 212 |
:from_revision => (cpmap.member?(p) ? le['node'] : nil)} |
|
| 208 | 213 |
end.sort { |a, b| a[:path] <=> b[:path] }
|
| 209 |
|
|
| 210 | 214 |
yield Revision.new(:revision => le['revision'], |
| 211 |
:scmid => le['node'], |
|
| 212 |
:author => (le['author']['__content__'] rescue ''), |
|
| 213 |
:time => Time.parse(le['date']['__content__']).localtime,
|
|
| 214 |
:message => le['msg']['__content__'], |
|
| 215 |
:paths => paths) |
|
| 215 |
:scmid => le['node'],
|
|
| 216 |
:author => (le['author']['__content__'] rescue ''),
|
|
| 217 |
:time => Time.parse(le['date']['__content__']),
|
|
| 218 |
:message => le['msg']['__content__'],
|
|
| 219 |
:paths => paths)
|
|
| 216 | 220 |
end |
| 217 | 221 |
self |
| 218 | 222 |
end |
| 219 | 223 |
|
| 224 |
# Returns list of nodes in the specified branch |
|
| 225 |
def nodes_in_branch(branch, options={})
|
|
| 226 |
hg_args = ['rhlog', '--template', '{node|short}\n', '--rhbranch', CGI.escape(branch)]
|
|
| 227 |
hg_args << '--from' << CGI.escape(branch) |
|
| 228 |
hg_args << '--to' << '0' |
|
| 229 |
hg_args << '--limit' << options[:limit] if options[:limit] |
|
| 230 |
hg(*hg_args) { |io| io.readlines.map { |e| e.chomp } }
|
|
| 231 |
end |
|
| 232 |
|
|
| 220 | 233 |
def diff(path, identifier_from, identifier_to=nil) |
| 221 | 234 |
hg_args = %w|rhdiff| |
| 222 | 235 |
if identifier_to |
| ... | ... | |
| 241 | 254 |
|
| 242 | 255 |
def cat(path, identifier=nil) |
| 243 | 256 |
p = CGI.escape(scm_iconv(@path_encoding, 'UTF-8', path)) |
| 244 |
hg 'rhcat', '-r', hgrev(identifier), hgtarget(p) do |io|
|
|
| 257 |
hg 'rhcat', '-r', CGI.escape(hgrev(identifier)), hgtarget(p) do |io|
|
|
| 245 | 258 |
io.binmode |
| 246 | 259 |
io.read |
| 247 | 260 |
end |
| ... | ... | |
| 252 | 265 |
def annotate(path, identifier=nil) |
| 253 | 266 |
p = CGI.escape(scm_iconv(@path_encoding, 'UTF-8', path)) |
| 254 | 267 |
blame = Annotate.new |
| 255 |
hg 'rhannotate', '-ncu', '-r', hgrev(identifier), hgtarget(p) do |io|
|
|
| 268 |
hg 'rhannotate', '-ncu', '-r', CGI.escape(hgrev(identifier)), hgtarget(p) do |io|
|
|
| 256 | 269 |
io.each_line do |line| |
| 257 | 270 |
line.force_encoding('ASCII-8BIT') if line.respond_to?(:force_encoding)
|
| 258 | 271 |
next unless line =~ %r{^([^:]+)\s(\d+)\s([0-9a-f]+):\s(.*)$}
|
Also available in: Unified diff