##// END OF EJS Templates
scm: cvs: code clean up app/models/repository/cvs.rb....
scm: cvs: code clean up app/models/repository/cvs.rb. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@5376 e93f8b46-1217-0410-a6f0-8f06a7374b81

File last commit:

r4968:732ee35b2fdb
r5256:18431d3681e0
Show More
cvs_adapter.rb
439 lines | 15.2 KiB | text/x-ruby | RubyLexer
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 # redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'redmine/scm/adapters/abstract_adapter'
module Redmine
module Scm
module Adapters
class CvsAdapter < AbstractAdapter
# CVS executable name
Toshi MARUYAMA
scm: change key name of configurable command name (#7517, #6159, #7047)....
r4677 CVS_BIN = Redmine::Configuration['scm_cvs_command'] || "cvs"
Toshi MARUYAMA
scm: use shell quote for scm command at adapter level (#7517, #4273)....
r4701
Toshi MARUYAMA
scm: cvs: add new method 'scm_cmd' to wrap shellout....
r4962 # raised if scm command exited with error, e.g. unknown revision.
class ScmCommandAborted < CommandFailed; end
Toshi MARUYAMA
scm: use shell quote for scm command at adapter level (#7517, #4273)....
r4701 class << self
def client_command
@@bin ||= CVS_BIN
end
def sq_bin
@@sq_bin ||= shell_quote(CVS_BIN)
end
Toshi MARUYAMA
scm: cvs: add methods of getting cvs version and add unit lib test (#4273)....
r4712
def client_version
@@client_version ||= (scm_command_version || [])
end
def client_available
Toshi MARUYAMA
scm: cvs: set client available if cvs version above 1.12....
r4715 client_version_above?([1, 12])
Toshi MARUYAMA
scm: cvs: add methods of getting cvs version and add unit lib test (#4273)....
r4712 end
def scm_command_version
Toshi MARUYAMA
scm: fix unit tests fails in Ruby 1.9....
r4872 scm_version = scm_version_from_command_line.dup
Toshi MARUYAMA
scm: Ruby 1.9 compatibility in getting scm version (#4273)....
r4800 if scm_version.respond_to?(:force_encoding)
scm_version.force_encoding('ASCII-8BIT')
end
Toshi MARUYAMA
scm: cvs: add methods of getting cvs version and add unit lib test (#4273)....
r4712 if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)}m)
m[2].scan(%r{\d+}).collect(&:to_i)
end
end
def scm_version_from_command_line
shellout("#{sq_bin} --version") { |io| io.read }.to_s
end
Toshi MARUYAMA
scm: use shell quote for scm command at adapter level (#7517, #4273)....
r4701 end
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 # Guidelines for the input:
# url -> the project-path, relative to the cvsroot (eg. module name)
# root_url -> the good old, sometimes damned, CVSROOT
# login -> unnecessary
# password -> unnecessary too
Toshi MARUYAMA
scm: update adapter initialize() to use path encoding (#2664, #2274)....
r4821 def initialize(url, root_url=nil, login=nil, password=nil,
path_encoding=nil)
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 @url = url
@login = login if login && !login.empty?
@password = (password || "") if @login
#TODO: better Exception here (IllegalArgumentException)
raise CommandFailed if root_url.blank?
@root_url = root_url
end
Toshi MARUYAMA
scm: use shell quote for scm command at adapter level (#7517, #4273)....
r4701
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 def root_url
@root_url
end
Toshi MARUYAMA
scm: use shell quote for scm command at adapter level (#7517, #4273)....
r4701
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 def url
@url
end
Toshi MARUYAMA
scm: use shell quote for scm command at adapter level (#7517, #4273)....
r4701
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 def info
logger.debug "<cvs> info"
Info.new({:root_url => @root_url, :lastrev => nil})
end
Toshi MARUYAMA
scm: use shell quote for scm command at adapter level (#7517, #4273)....
r4701
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 def get_previous_revision(revision)
CvsRevisionHelper.new(revision).prevRev
end
Toshi MARUYAMA
scm: cvs: code clean up....
r4684
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 # Returns an Entries collection
# or nil if the given path doesn't exist in the repository
# this method is used by the repository-browser (aka LIST)
def entries(path=nil, identifier=nil)
logger.debug "<cvs> entries '#{path}' with identifier '#{identifier}'"
path_with_project="#{url}#{with_leading_slash(path)}"
entries = Entries.new
Toshi MARUYAMA
scm: cvs: use scm_cmd() in adapter entries()....
r4963 cmd_args = %w|rls -e|
Toshi MARUYAMA
scm: cvs: use "-D time_to_cvstime_rlog(identifier)" in adapter entries()....
r4967 cmd_args << "-D" << time_to_cvstime_rlog(identifier) if identifier
Toshi MARUYAMA
scm: cvs: use scm_cmd() in adapter entries()....
r4963 cmd_args << path_with_project
scm_cmd(*cmd_args) do |io|
Toshi MARUYAMA
scm: cvs: code clean up adapter entries()....
r4966 io.each_line() do |line|
fields = line.chop.split('/',-1)
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 logger.debug(">>InspectLine #{fields.inspect}")
if fields[0]!="D"
Toshi MARUYAMA
scm: cvs: fix age column on repository view is off by timezone delta (#7827)....
r4968 time = nil
# Thu Dec 13 16:27:22 2007
time_l = fields[-3].split(' ')
if time_l.size == 5 && time_l[4].length == 4
begin
time = Time.parse(
"#{time_l[1]} #{time_l[2]} #{time_l[3]} GMT #{time_l[4]}")
rescue
end
end
Toshi MARUYAMA
scm: cvs: code clean up adapter entries()....
r4966 entries << Entry.new(
{
:name => fields[-5],
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 #:path => fields[-4].include?(path)?fields[-4]:(path + "/"+ fields[-4]),
:path => "#{path}/#{fields[-5]}",
:kind => 'file',
:size => nil,
Toshi MARUYAMA
scm: cvs: code clean up adapter entries()....
r4966 :lastrev => Revision.new(
{
:revision => fields[-4],
:name => fields[-4],
Toshi MARUYAMA
scm: cvs: fix age column on repository view is off by timezone delta (#7827)....
r4968 :time => time,
Toshi MARUYAMA
scm: cvs: code clean up adapter entries()....
r4966 :author => ''
})
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 })
else
Toshi MARUYAMA
scm: cvs: code clean up adapter entries()....
r4966 entries << Entry.new(
{
:name => fields[1],
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 :path => "#{path}/#{fields[1]}",
:kind => 'dir',
:size => nil,
:lastrev => nil
Toshi MARUYAMA
scm: cvs: code clean up adapter entries()....
r4966 })
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 end
Toshi MARUYAMA
scm: cvs: code clean up adapter entries()....
r4966 end
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 end
entries.sort_by_name
Toshi MARUYAMA
scm: cvs: use scm_cmd() in adapter entries()....
r4963 rescue ScmCommandAborted
nil
Toshi MARUYAMA
scm: cvs: code clean up....
r4684 end
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556
STARTLOG="----------------------------"
ENDLOG ="============================================================================="
Toshi MARUYAMA
scm: cvs: code clean up....
r4684
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 # Returns all revisions found between identifier_from and identifier_to
# in the repository. both identifier have to be dates or nil.
# these method returns nothing but yield every result in block
def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}, &block)
logger.debug "<cvs> revisions path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
Toshi MARUYAMA
scm: cvs: code clean up....
r4684
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 path_with_project="#{url}#{with_leading_slash(path)}"
Toshi MARUYAMA
scm: cvs: use scm_cmd() in adapter revisions()....
r4965 cmd_args = %w|rlog|
cmd_args << "-d" << ">#{time_to_cvstime_rlog(identifier_from)}" if identifier_from
cmd_args << path_with_project
scm_cmd(*cmd_args) do |io|
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 state="entry_start"
commit_log=String.new
revision=nil
date=nil
author=nil
entry_path=nil
entry_name=nil
file_state=nil
branch_map=nil
Toshi MARUYAMA
scm: cvs: code clean up....
r4684 io.each_line() do |line|
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556
if state!="revision" && /^#{ENDLOG}/ =~ line
commit_log=String.new
revision=nil
state="entry_start"
end
if state=="entry_start"
branch_map=Hash.new
Jean-Philippe Lang
Fixed: CVS connexion string may not contain @....
r2094 if /^RCS file: #{Regexp.escape(root_url_path)}\/#{Regexp.escape(path_with_project)}(.+),v$/ =~ line
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 entry_path = normalize_cvs_path($1)
entry_name = normalize_path(File.basename($1))
logger.debug("Path #{entry_path} <=> Name #{entry_name}")
elsif /^head: (.+)$/ =~ line
entry_headRev = $1 #unless entry.nil?
elsif /^symbolic names:/ =~ line
state="symbolic" #unless entry.nil?
elsif /^#{STARTLOG}/ =~ line
commit_log=String.new
state="revision"
end
next
elsif state=="symbolic"
if /^(.*):\s(.*)/ =~ (line.strip)
branch_map[$1]=$2
else
state="tags"
next
end
elsif state=="tags"
if /^#{STARTLOG}/ =~ line
commit_log = ""
state="revision"
elsif /^#{ENDLOG}/ =~ line
state="head"
end
next
elsif state=="revision"
Toshi MARUYAMA
scm: cvs: code clean up....
r4684 if /^#{ENDLOG}/ =~ line || /^#{STARTLOG}/ =~ line
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 if revision
Toshi MARUYAMA
scm: cvs: code clean up....
r4684
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 revHelper=CvsRevisionHelper.new(revision)
revBranch="HEAD"
branch_map.each() do |branch_name,branch_point|
if revHelper.is_in_branch_with_symbol(branch_point)
revBranch=branch_name
end
end
logger.debug("********** YIELD Revision #{revision}::#{revBranch}")
Toshi MARUYAMA
scm: cvs: code clean up....
r4684 yield Revision.new({
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 :time => date,
:author => author,
:message=>commit_log.chomp,
:paths => [{
:revision => revision,
:branch=> revBranch,
:path=>entry_path,
:name=>entry_name,
:kind=>'file',
:action=>file_state
}]
Toshi MARUYAMA
scm: cvs: code clean up....
r4684 })
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 end
Toshi MARUYAMA
scm: cvs: code clean up....
r4684
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 commit_log=String.new
revision=nil
if /^#{ENDLOG}/ =~ line
state="entry_start"
end
next
end
Toshi MARUYAMA
scm: cvs: code clean up....
r4684
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 if /^branches: (.+)$/ =~ line
#TODO: version.branch = $1
elsif /^revision (\d+(?:\.\d+)+).*$/ =~ line
revision = $1
elsif /^date:\s+(\d+.\d+.\d+\s+\d+:\d+:\d+)/ =~ line
date = Time.parse($1)
author = /author: ([^;]+)/.match(line)[1]
file_state = /state: ([^;]+)/.match(line)[1]
#TODO: linechanges only available in CVS.... maybe a feature our SVN implementation. i'm sure, they are
# useful for stats or something else
# linechanges =/lines: \+(\d+) -(\d+)/.match(line)
# unless linechanges.nil?
# version.line_plus = linechanges[1]
# version.line_minus = linechanges[2]
# else
# version.line_plus = 0
Toshi MARUYAMA
scm: cvs: code clean up....
r4684 # version.line_minus = 0
# end
else
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 commit_log << line unless line =~ /^\*\*\* empty log message \*\*\*/
Toshi MARUYAMA
scm: cvs: code clean up....
r4684 end
end
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 end
end
Toshi MARUYAMA
scm: cvs: use scm_cmd() in adapter revisions()....
r4965 rescue ScmCommandAborted
Revisions.new
Toshi MARUYAMA
scm: cvs: code clean up....
r4684 end
Jean-Philippe Lang
Move unified diff parser out of the scm abstract adapter so it can be reused for viewing attached diffs (#1403)....
r1499 def diff(path, identifier_from, identifier_to=nil)
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 logger.debug "<cvs> diff path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
path_with_project="#{url}#{with_leading_slash(path)}"
Toshi MARUYAMA
scm: use shell quote for scm command at adapter level (#7517, #4273)....
r4701 cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rdiff -u -r#{identifier_to} -r#{identifier_from} #{shell_quote path_with_project}"
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 diff = []
shellout(cmd) do |io|
io.each_line do |line|
diff << line
end
end
return nil if $? && $?.exitstatus != 0
Jean-Philippe Lang
Move unified diff parser out of the scm abstract adapter so it can be reused for viewing attached diffs (#1403)....
r1499 diff
Toshi MARUYAMA
scm: cvs: code clean up....
r4684 end
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 def cat(path, identifier=nil)
identifier = (identifier) ? identifier : "HEAD"
logger.debug "<cvs> cat path:'#{path}',identifier #{identifier}"
path_with_project="#{url}#{with_leading_slash(path)}"
Toshi MARUYAMA
scm: use shell quote for scm command at adapter level (#7517, #4273)....
r4701 cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} co"
Jean-Philippe Lang
Fixed: view file at given revision with CVS....
r1539 cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier
cmd << " -p #{shell_quote path_with_project}"
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 cat = nil
shellout(cmd) do |io|
Toshi MARUYAMA
scm: cvs: fix most binary files become corrupted on Windows (#6090)....
r4695 io.binmode
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 cat = io.read
end
return nil if $? && $?.exitstatus != 0
cat
Toshi MARUYAMA
scm: cvs: code clean up....
r4684 end
Jean-Philippe Lang
Added Annotate/Blame view for Subversion, CVS and Mercurial repositories....
r934
def annotate(path, identifier=nil)
Jean-Philippe Lang
Fixed that some arguments where not properly escaped in scm adapters....
r4425 identifier = (identifier) ? identifier.to_i : "HEAD"
Jean-Philippe Lang
Added Annotate/Blame view for Subversion, CVS and Mercurial repositories....
r934 logger.debug "<cvs> annotate path:'#{path}',identifier #{identifier}"
path_with_project="#{url}#{with_leading_slash(path)}"
Toshi MARUYAMA
scm: use shell quote for scm command at adapter level (#7517, #4273)....
r4701 cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rannotate -r#{identifier} #{shell_quote path_with_project}"
Jean-Philippe Lang
Added Annotate/Blame view for Subversion, CVS and Mercurial repositories....
r934 blame = Annotate.new
shellout(cmd) do |io|
io.each_line do |line|
next unless line =~ %r{^([\d\.]+)\s+\(([^\)]+)\s+[^\)]+\):\s(.*)$}
blame.add_line($3.rstrip, Revision.new(:revision => $1, :author => $2.strip))
end
end
return nil if $? && $?.exitstatus != 0
blame
end
Toshi MARUYAMA
scm: cvs: code clean up....
r4684
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 private
Toshi MARUYAMA
scm: cvs: code clean up....
r4684
Jean-Philippe Lang
Fixed: CVS connexion string may not contain @....
r2094 # Returns the root url without the connexion string
# :pserver:anonymous@foo.bar:/path => /path
# :ext:cvsservername:/path => /path
def root_url_path
root_url.to_s.gsub(/^:.+:\d*/, '')
end
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556
# convert a date/time into the CVS-format
def time_to_cvstime(time)
return nil if time.nil?
Jean-Philippe Lang
ruby1.9 compatibility...
r4481 return Time.now if time == 'HEAD'
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 unless time.kind_of? Time
time = Time.parse(time)
end
return time.strftime("%Y-%m-%d %H:%M:%S")
end
Toshi MARUYAMA
scm: cvs: use localtime at cvs rlog -d option (#996, #3761)....
r4674
def time_to_cvstime_rlog(time)
return nil if time.nil?
t1 = time.clone.localtime
return t1.strftime("%Y-%m-%d %H:%M:%S")
end
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556
def normalize_cvs_path(path)
normalize_path(path.gsub(/Attic\//,''))
end
def normalize_path(path)
path.sub(/^(\/)*(.*)/,'\2').sub(/(.*)(,v)+/,'\1')
end
Toshi MARUYAMA
scm: cvs: add new method 'scm_cmd' to wrap shellout....
r4962
def scm_cmd(*args, &block)
full_args = [CVS_BIN, '-d', root_url]
full_args += args
ret = shellout(full_args.map { |e| shell_quote e.to_s }.join(' '), &block)
if $? && $?.exitstatus != 0
raise ScmCommandAborted, "cvs exited with non-zero status: #{$?.exitstatus}"
end
ret
end
private :scm_cmd
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 end
Toshi MARUYAMA
scm: cvs: add new method 'scm_cmd' to wrap shellout....
r4962
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 class CvsRevisionHelper
attr_accessor :complete_rev, :revision, :base, :branchid
def initialize(complete_rev)
@complete_rev = complete_rev
parseRevision()
end
def branchPoint
return @base
end
def branchVersion
if isBranchRevision
return @base+"."+@branchid
end
return @base
end
def isBranchRevision
!@branchid.nil?
end
def prevRev
unless @revision==0
return buildRevision(@revision-1)
end
return buildRevision(@revision)
end
def is_in_branch_with_symbol(branch_symbol)
bpieces=branch_symbol.split(".")
branch_start="#{bpieces[0..-3].join(".")}.#{bpieces[-1]}"
return (branchVersion==branch_start)
end
private
def buildRevision(rev)
if rev== 0
Toshi MARUYAMA
scm: cvs: fix CVS diffs do not handle new files properly (#7615)....
r4819 if @branchid.nil?
@base+".0"
else
@base
end
Jean-Philippe Lang
Added basic support for CVS and Mercurial SCMs....
r556 elsif @branchid.nil?
@base+"."+rev.to_s
else
@base+"."+@branchid+"."+rev.to_s
end
end
# Interpretiert die cvs revisionsnummern wie z.b. 1.14 oder 1.3.0.15
def parseRevision()
pieces=@complete_rev.split(".")
@revision=pieces.last.to_i
baseSize=1
baseSize+=(pieces.size/2)
@base=pieces[0..-baseSize].join(".")
if baseSize > 2
@branchid=pieces[-2]
end
end
end
end
end
end