Commit 86faa88e authored by Eric Davis's avatar Eric Davis

Merge branch 'ticket/master/225-upstream-scm'

parents 8d6133f6 6d0fcf55
/.project
/.loadpath
/config/additional_environment.rb
/config/configuration.yml
/config/database.yml
......
syntax: glob
.project
.loadpath
config/additional_environment.rb
config/configuration.yml
config/database.yml
......
......@@ -67,13 +67,13 @@ class RepositoriesController < ApplicationController
redirect_to :action => 'committers', :id => @project
end
end
def destroy
@repository.destroy
redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'repository'
end
def show
def show
@repository.fetch_changesets if Setting.autofetch_changesets? && @path.empty?
@entries = @repository.entries(@path, @rev)
......@@ -88,7 +88,7 @@ class RepositoriesController < ApplicationController
end
alias_method :browse, :show
def changes
@entry = @repository.entry(@path, @rev)
(show_error_not_found; return) unless @entry
......@@ -96,23 +96,23 @@ class RepositoriesController < ApplicationController
@properties = @repository.properties(@path, @rev)
@changeset = @repository.find_changeset_by_name(@rev)
end
def revisions
@changeset_count = @repository.changesets.count
@changeset_pages = Paginator.new self, @changeset_count,
per_page_option,
params['page']
per_page_option,
params['page']
@changesets = @repository.changesets.find(:all,
:limit => @changeset_pages.items_per_page,
:offset => @changeset_pages.current.offset,
:include => [:user, :repository])
:limit => @changeset_pages.items_per_page,
:offset => @changeset_pages.current.offset,
:include => [:user, :repository])
respond_to do |format|
format.html { render :layout => false if request.xhr? }
format.atom { render_feed(@changesets, :title => "#{@project.name}: #{l(:label_revision_plural)}") }
end
end
def entry
@entry = @repository.entry(@path, @rev)
(show_error_not_found; return) unless @entry
......@@ -122,9 +122,10 @@ class RepositoriesController < ApplicationController
@content = @repository.cat(@path, @rev)
(show_error_not_found; return) unless @content
if 'raw' == params[:format] || @content.is_binary_data? || (@entry.size && @entry.size > Setting.file_max_size_displayed.to_i.kilobyte)
if 'raw' == params[:format] || @content.is_binary_data? ||
(@entry.size && @entry.size > Setting.file_max_size_displayed.to_i.kilobyte)
# Force the download
send_data @content, :filename => @path.split('/').last
send_data @content, :filename => filename_for_content_disposition(@path.split('/').last)
else
# Prevent empty lines when displaying a file with Windows style eol
@content.gsub!("\r\n", "\n")
......@@ -135,7 +136,7 @@ class RepositoriesController < ApplicationController
def annotate
@entry = @repository.entry(@path, @rev)
(show_error_not_found; return) unless @entry
@annotate = @repository.scm.annotate(@path, @rev)
(render_error l(:error_scm_annotate); return) if @annotate.nil? || @annotate.empty?
@changeset = @repository.find_changeset_by_name(@rev)
......@@ -153,7 +154,7 @@ class RepositoriesController < ApplicationController
rescue ChangesetNotFound
show_error_not_found
end
def diff
if params[:format] == 'diff'
@diff = @repository.diff(@path, @rev, @rev_to)
......@@ -185,11 +186,11 @@ class RepositoriesController < ApplicationController
end
end
def stats
def stats
end
def graph
data = nil
data = nil
case params[:graph]
when "commits_per_month"
data = graph_commits_per_month(@repository)
......
......@@ -25,6 +25,10 @@ module Redmine
class AbstractAdapter #:nodoc:
class << self
def client_command
""
end
# Returns the version of the scm client
# Eg: [1, 5, 0] or [] if unknown
def client_version
......@@ -45,8 +49,20 @@ module Redmine
def client_version_above?(v, options={})
((client_version <=> v) >= 0) || (client_version.empty? && options[:unknown])
end
def client_available
true
end
def shell_quote(str)
if Redmine::Platform.mswin?
'"' + str.gsub(/"/, '\\"') + '"'
else
"'" + str.gsub(/'/, "'\"'\"'") + "'"
end
end
end
def initialize(url, root_url=nil, login=nil, password=nil)
@url = url
@login = login if login && !login.empty?
......@@ -138,7 +154,7 @@ module Redmine
path ||= ''
(path[-1,1] == "/") ? path : "#{path}/"
end
def without_leading_slash(path)
path ||= ''
path.gsub(%r{^/+}, '')
......@@ -148,13 +164,9 @@ module Redmine
path ||= ''
(path[-1,1] == "/") ? path[0..-2] : path
end
def shell_quote(str)
if Redmine::Platform.mswin?
'"' + str.gsub(/"/, '\\"') + '"'
else
"'" + str.gsub(/'/, "'\"'\"'") + "'"
end
self.class.shell_quote(str)
end
private
......@@ -168,11 +180,11 @@ module Redmine
base = path.match(/^\//) ? root_url : url
shell_quote("#{base}/#{path}".gsub(/[?<>\*]/, ''))
end
def logger
self.class.logger
end
def shellout(cmd, &block)
self.class.shellout(cmd, &block)
end
......
......@@ -19,15 +19,25 @@ require 'redmine/scm/adapters/abstract_adapter'
module Redmine
module Scm
module Adapters
module Adapters
class BazaarAdapter < AbstractAdapter
# Bazaar executable name
BZR_BIN = Redmine::Configuration['scm_bazaar_command'] || "bzr"
class << self
def client_command
@@bin ||= BZR_BIN
end
def sq_bin
@@sq_bin ||= shell_quote(BZR_BIN)
end
end
# Get info about the repository
def info
cmd = "#{BZR_BIN} revno #{target('')}"
cmd = "#{self.class.sq_bin} revno #{target('')}"
info = nil
shellout(cmd) do |io|
if io.read =~ %r{^(\d+)\r?$}
......@@ -43,13 +53,13 @@ module Redmine
rescue CommandFailed
return nil
end
# Returns an Entries collection
# or nil if the given path doesn't exist in the repository
def entries(path=nil, identifier=nil)
path ||= ''
entries = Entries.new
cmd = "#{BZR_BIN} ls -v --show-ids"
cmd = "#{self.class.sq_bin} ls -v --show-ids"
identifier = -1 unless identifier && identifier.to_i > 0
cmd << " -r#{identifier.to_i}"
cmd << " #{target(path)}"
......@@ -71,13 +81,13 @@ module Redmine
logger.debug("Found #{entries.size} entries in the repository for #{target(path)}") if logger && logger.debug?
entries.sort_by_name
end
def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
path ||= ''
identifier_from = (identifier_from and identifier_from.to_i > 0) ? identifier_from.to_i : 'last:1'
identifier_to = (identifier_to and identifier_to.to_i > 0) ? identifier_to.to_i : 1
revisions = Revisions.new
cmd = "#{BZR_BIN} log -v --show-ids -r#{identifier_to}..#{identifier_from} #{target(path)}"
cmd = "#{self.class.sq_bin} log -v --show-ids -r#{identifier_to}..#{identifier_from} #{target(path)}"
shellout(cmd) do |io|
revision = nil
parsing = nil
......@@ -132,7 +142,7 @@ module Redmine
return nil if $? && $?.exitstatus != 0
revisions
end
def diff(path, identifier_from, identifier_to=nil)
path ||= ''
if identifier_to
......@@ -143,7 +153,7 @@ module Redmine
if identifier_from
identifier_from = identifier_from.to_i
end
cmd = "#{BZR_BIN} diff -r#{identifier_to}..#{identifier_from} #{target(path)}"
cmd = "#{self.class.sq_bin} diff -r#{identifier_to}..#{identifier_from} #{target(path)}"
diff = []
shellout(cmd) do |io|
io.each_line do |line|
......@@ -153,9 +163,9 @@ module Redmine
#return nil if $? && $?.exitstatus != 0
diff
end
def cat(path, identifier=nil)
cmd = "#{BZR_BIN} cat"
cmd = "#{self.class.sq_bin} cat"
cmd << " -r#{identifier.to_i}" if identifier && identifier.to_i > 0
cmd << " #{target(path)}"
cat = nil
......@@ -166,9 +176,9 @@ module Redmine
return nil if $? && $?.exitstatus != 0
cat
end
def annotate(path, identifier=nil)
cmd = "#{BZR_BIN} annotate --all"
cmd = "#{self.class.sq_bin} annotate --all"
cmd << " -r#{identifier.to_i}" if identifier && identifier.to_i > 0
cmd << " #{target(path)}"
blame = Annotate.new
......
......@@ -24,7 +24,17 @@ module Redmine
# CVS executable name
CVS_BIN = Redmine::Configuration['scm_cvs_command'] || "cvs"
class << self
def client_command
@@bin ||= CVS_BIN
end
def sq_bin
@@sq_bin ||= shell_quote(CVS_BIN)
end
end
# Guidelines for the input:
# url -> the project-path, relative to the cvsroot (eg. module name)
# root_url -> the good old, sometimes damned, CVSROOT
......@@ -38,24 +48,24 @@ module Redmine
raise CommandFailed if root_url.blank?
@root_url = root_url
end
def root_url
@root_url
end
def url
@url
end
def info
logger.debug "<cvs> info"
Info.new({:root_url => @root_url, :lastrev => nil})
end
def get_previous_revision(revision)
CvsRevisionHelper.new(revision).prevRev
end
# 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)
......@@ -63,14 +73,14 @@ module Redmine
logger.debug "<cvs> entries '#{path}' with identifier '#{identifier}'"
path_with_project="#{url}#{with_leading_slash(path)}"
entries = Entries.new
cmd = "#{CVS_BIN} -d #{shell_quote root_url} rls -e"
cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rls -e"
cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier
cmd << " #{shell_quote path_with_project}"
shellout(cmd) do |io|
io.each_line(){|line|
fields=line.chop.split('/',-1)
logger.debug(">>InspectLine #{fields.inspect}")
if fields[0]!="D"
entries << Entry.new({:name => fields[-5],
#:path => fields[-4].include?(path)?fields[-4]:(path + "/"+ fields[-4]),
......@@ -96,19 +106,19 @@ module Redmine
end
return nil if $? && $?.exitstatus != 0
entries.sort_by_name
end
end
STARTLOG="----------------------------"
ENDLOG ="============================================================================="
# 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}"
path_with_project="#{url}#{with_leading_slash(path)}"
cmd = "#{CVS_BIN} -d #{shell_quote root_url} rlog"
cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rlog"
cmd << " -d\">#{time_to_cvstime_rlog(identifier_from)}\"" if identifier_from
cmd << " #{shell_quote path_with_project}"
shellout(cmd) do |io|
......@@ -123,7 +133,7 @@ module Redmine
file_state=nil
branch_map=nil
io.each_line() do |line|
io.each_line() do |line|
if state!="revision" && /^#{ENDLOG}/ =~ line
commit_log=String.new
......@@ -162,9 +172,9 @@ module Redmine
end
next
elsif state=="revision"
if /^#{ENDLOG}/ =~ line || /^#{STARTLOG}/ =~ line
if /^#{ENDLOG}/ =~ line || /^#{STARTLOG}/ =~ line
if revision
revHelper=CvsRevisionHelper.new(revision)
revBranch="HEAD"
......@@ -176,7 +186,7 @@ module Redmine
logger.debug("********** YIELD Revision #{revision}::#{revBranch}")
yield Revision.new({
yield Revision.new({
:time => date,
:author => author,
:message=>commit_log.chomp,
......@@ -188,9 +198,9 @@ module Redmine
:kind=>'file',
:action=>file_state
}]
})
})
end
commit_log=String.new
revision=nil
......@@ -199,7 +209,7 @@ module Redmine
end
next
end
if /^branches: (.+)$/ =~ line
#TODO: version.branch = $1
elsif /^revision (\d+(?:\.\d+)+).*$/ =~ line
......@@ -216,20 +226,20 @@ module Redmine
# version.line_minus = linechanges[2]
# else
# version.line_plus = 0
# version.line_minus = 0
# end
else
# version.line_minus = 0
# end
else
commit_log << line unless line =~ /^\*\*\* empty log message \*\*\*/
end
end
end
end
end
end
end
end
def diff(path, identifier_from, identifier_to=nil)
logger.debug "<cvs> diff path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
path_with_project="#{url}#{with_leading_slash(path)}"
cmd = "#{CVS_BIN} -d #{shell_quote root_url} rdiff -u -r#{identifier_to} -r#{identifier_from} #{shell_quote path_with_project}"
cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rdiff -u -r#{identifier_to} -r#{identifier_from} #{shell_quote path_with_project}"
diff = []
shellout(cmd) do |io|
io.each_line do |line|
......@@ -238,28 +248,29 @@ module Redmine
end
return nil if $? && $?.exitstatus != 0
diff
end
end
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)}"
cmd = "#{CVS_BIN} -d #{shell_quote root_url} co"
cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} co"
cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier
cmd << " -p #{shell_quote path_with_project}"
cat = nil
shellout(cmd) do |io|
io.binmode
cat = io.read
end
return nil if $? && $?.exitstatus != 0
cat
end
end
def annotate(path, identifier=nil)
identifier = (identifier) ? identifier.to_i : "HEAD"
logger.debug "<cvs> annotate path:'#{path}',identifier #{identifier}"
path_with_project="#{url}#{with_leading_slash(path)}"
cmd = "#{CVS_BIN} -d #{shell_quote root_url} rannotate -r#{identifier} #{shell_quote path_with_project}"
cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rannotate -r#{identifier} #{shell_quote path_with_project}"
blame = Annotate.new
shellout(cmd) do |io|
io.each_line do |line|
......@@ -270,9 +281,9 @@ module Redmine
return nil if $? && $?.exitstatus != 0
blame
end
private
# Returns the root url without the connexion string
# :pserver:anonymous@foo.bar:/path => /path
# :ext:cvsservername:/path => /path
......
......@@ -20,16 +20,24 @@ require 'rexml/document'
module Redmine
module Scm
module Adapters
module Adapters
class DarcsAdapter < AbstractAdapter
# Darcs executable name
DARCS_BIN = Redmine::Configuration['scm_darcs_command'] || "darcs"
class << self
def client_command
@@bin ||= DARCS_BIN
end
def sq_bin
@@sq_bin ||= shell_quote(DARCS_BIN)
end
def client_version
@@client_version ||= (darcs_binary_version || [])
end
def darcs_binary_version
darcsversion = darcs_binary_version_from_command_line
if m = darcsversion.match(%r{\A(.*?)((\d+\.)+\d+)})
......@@ -38,7 +46,7 @@ module Redmine
end
def darcs_binary_version_from_command_line
shellout("#{DARCS_BIN} --version") { |io| io.read }.to_s
shellout("#{sq_bin} --version") { |io| io.read }.to_s
end
end
......@@ -57,7 +65,7 @@ module Redmine
rev = revisions(nil,nil,nil,{:limit => 1})
rev ? Info.new({:root_url => @url, :lastrev => rev.last}) : nil
end
# Returns an Entries collection
# or nil if the given path doesn't exist in the repository
def entries(path=nil, identifier=nil)
......@@ -66,7 +74,7 @@ module Redmine
path = ( self.class.client_version_above?([2, 2, 0]) ? @url : '.' )
end
entries = Entries.new
cmd = "#{DARCS_BIN} annotate --repodir #{shell_quote @url} --xml-output"
cmd = "#{self.class.sq_bin} annotate --repodir #{shell_quote @url} --xml-output"
cmd << " --match #{shell_quote("hash #{identifier}")}" if identifier
cmd << " #{shell_quote path}"
shellout(cmd) do |io|
......@@ -86,11 +94,11 @@ module Redmine
return nil if $? && $?.exitstatus != 0
entries.compact.sort_by_name
end
def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
path = '.' if path.blank?
revisions = Revisions.new
cmd = "#{DARCS_BIN} changes --repodir #{shell_quote @url} --xml-output"
cmd = "#{self.class.sq_bin} changes --repodir #{shell_quote @url} --xml-output"
cmd << " --from-match #{shell_quote("hash #{identifier_from}")}" if identifier_from
cmd << " --last #{options[:limit].to_i}" if options[:limit]
shellout(cmd) do |io|
......@@ -113,10 +121,10 @@ module Redmine
return nil if $? && $?.exitstatus != 0
revisions
end
def diff(path, identifier_from, identifier_to=nil)
path = '*' if path.blank?
cmd = "#{DARCS_BIN} diff --repodir #{shell_quote @url}"
cmd = "#{self.class.sq_bin} diff --repodir #{shell_quote @url}"
if identifier_to.nil?
cmd << " --match #{shell_quote("hash #{identifier_from}")}"
else
......@@ -133,9 +141,9 @@ module Redmine
return nil if $? && $?.exitstatus != 0
diff
end
def cat(path, identifier=nil)
cmd = "#{DARCS_BIN} show content --repodir #{shell_quote @url}"
cmd = "#{self.class.sq_bin} show content --repodir #{shell_quote @url}"
cmd << " --match #{shell_quote("hash #{identifier}")}" if identifier
cmd << " #{shell_quote path}"
cat = nil
......@@ -148,7 +156,7 @@ module Redmine
end
private
# Returns an Entry from the given XML element
# or nil if the entry was deleted
def entry_from_xml(element, path_prefix)
......@@ -196,10 +204,10 @@ module Redmine
end
paths
end
# Retrieve changed paths for a single patch
def get_paths_for_patch_raw(hash)
cmd = "#{DARCS_BIN} annotate --repodir #{shell_quote @url} --summary --xml-output"
cmd = "#{self.class.sq_bin} annotate --repodir #{shell_quote @url} --summary --xml-output"
cmd << " --match #{shell_quote("hash #{hash}")} "
paths = []
shellout(cmd) do |io|
......
......@@ -25,7 +25,12 @@ module Redmine
module Scm
module Adapters
class FilesystemAdapter < AbstractAdapter
class << self
def client_available
true
end
end
def initialize(url, root_url=nil, login=nil, password=nil)
@url = with_trailling_slash(url)
......
......@@ -19,11 +19,25 @@ require 'redmine/scm/adapters/abstract_adapter'
module Redmine
module Scm
module Adapters
module Adapters
class GitAdapter < AbstractAdapter
# Git executable name
GIT_BIN = Redmine::Configuration['scm_git_command'] || "git"
class << self
def client_command
@@bin ||= GIT_BIN
end
def sq_bin
@@sq_bin ||= shell_quote(GIT_BIN)
end
def client_available
!client_version.empty?
end
end
def info
begin
Info.new(:root_url => url, :lastrev => lastrev('',nil))
......@@ -35,7 +49,7 @@ module Redmine
def branches
return @branches if @branches
@branches = []
cmd = "#{GIT_BIN} --git-dir #{target('')} branch --no-color"
cmd = "#{self.class.sq_bin} --git-dir #{target('')} branch --no-color"
shellout(cmd) do |io|
io.each_line do |line|
@branches << line.match('\s*\*?\s*(.*)$')[1]
......@@ -46,7 +60,7 @@ module Redmine
def tags
return @tags if @tags
cmd = "#{GIT_BIN} --git-dir #{target('')} tag"
cmd = "#{self.class.sq_bin} --git-dir #{target('')} tag"
shellout(cmd) do |io|
@tags = io.readlines.sort!.map{|t| t.strip}
end
......@@ -59,7 +73,7 @@ module Redmine
def entries(path=nil, identifier=nil)
path ||= ''
entries = Entries.new
cmd = "#{GIT_BIN} --git-dir #{target('')} ls-tree -l "
cmd = "#{self.class.sq_bin} --git-dir #{target('')} ls-tree -l "
cmd << shell_quote("HEAD:" + path) if identifier.nil?
cmd << shell_quote(identifier + ":" + path) if identifier
shellout(cmd) do |io|
......@@ -86,7 +100,7 @@ module Redmine
def lastrev(path,rev)
return nil if path.nil?
cmd = "#{GIT_BIN} --git-dir #{target('')} log --no-color --date=iso --pretty=fuller --no-merges -n 1 "
cmd = "#{self.class.sq_bin} --git-dir #{target('')} log --no-color --date=iso --pretty=fuller --no-merges -n 1 "
cmd << " #{shell_quote rev} " if rev
cmd << "-- #{shell_quote path} " unless path.empty?
lines = []
......@@ -114,7 +128,7 @@ module Redmine
def revisions(path, identifier_from, identifier_to, options={})
revisions = Revisions.new
cmd = "#{GIT_BIN} --git-dir #{target('')} log --no-color --raw --date=iso --pretty=fuller "
cmd = "#{self.class.sq_bin} --git-dir #{target('')} log --no-color --raw --date=iso --pretty=fuller "
cmd << " --reverse " if options[:reverse]
cmd << " --all " if options[:all]
cmd << " -n #{options[:limit].to_i} " if options[:limit]
......@@ -209,9 +223,9 @@ module Redmine
path ||= ''
if identifier_to
cmd = "#{GIT_BIN} --git-dir #{target('')} diff --no-color #{shell_quote identifier_to} #{shell_quote identifier_from}"
cmd = "#{self.class.sq_bin} --git-dir #{target('')} diff --no-color #{shell_quote identifier_to} #{shell_quote identifier_from}"
else
cmd = "#{GIT_BIN} --git-dir #{target('')} show --no-color #{shell_quote identifier_from}"