summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/helpers/repositories_helper.rb4
-rw-r--r--app/models/repository/bazaar.rb86
-rw-r--r--app/views/repositories/revisions.rhtml6
-rw-r--r--lib/redmine.rb2
-rw-r--r--lib/redmine/scm/adapters/bazaar_adapter.rb204
5 files changed, 298 insertions, 4 deletions
diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb
index 333b30b1c..d2d04604d 100644
--- a/app/helpers/repositories_helper.rb
+++ b/app/helpers/repositories_helper.rb
@@ -80,4 +80,8 @@ module RepositoriesHelper
content_tag('p', form.text_field(:root_url, :label => 'CVSROOT', :size => 60, :required => true, :disabled => !repository.new_record?)) +
content_tag('p', form.text_field(:url, :label => 'Module', :size => 30, :required => true, :disabled => !repository.new_record?))
end
+
+ def bazaar_field_tags(form, repository)
+ content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.new_record?)))
+ end
end
diff --git a/app/models/repository/bazaar.rb b/app/models/repository/bazaar.rb
new file mode 100644
index 000000000..6e387f957
--- /dev/null
+++ b/app/models/repository/bazaar.rb
@@ -0,0 +1,86 @@
+# 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/bazaar_adapter'
+
+class Repository::Bazaar < Repository
+ attr_protected :root_url
+ validates_presence_of :url
+
+ def scm_adapter
+ Redmine::Scm::Adapters::BazaarAdapter
+ end
+
+ def self.scm_name
+ 'Bazaar'
+ end
+
+ def entries(path=nil, identifier=nil)
+ entries = scm.entries(path, identifier)
+ if entries
+ entries.each do |e|
+ next if e.lastrev.revision.blank?
+ c = Change.find(:first,
+ :include => :changeset,
+ :conditions => ["#{Change.table_name}.revision = ? and #{Changeset.table_name}.repository_id = ?", e.lastrev.revision, id],
+ :order => "#{Changeset.table_name}.revision DESC")
+ if c
+ e.lastrev.identifier = c.changeset.revision
+ e.lastrev.name = c.changeset.revision
+ e.lastrev.author = c.changeset.committer
+ end
+ end
+ end
+ end
+
+ def fetch_changesets
+ scm_info = scm.info
+ if scm_info
+ # latest revision found in database
+ db_revision = latest_changeset ? latest_changeset.revision : 0
+ # latest revision in the repository
+ scm_revision = scm_info.lastrev.identifier.to_i
+ if db_revision < scm_revision
+ logger.debug "Fetching changesets for repository #{url}" if logger && logger.debug?
+ identifier_from = db_revision + 1
+ while (identifier_from <= scm_revision)
+ # loads changesets by batches of 200
+ identifier_to = [identifier_from + 199, scm_revision].min
+ revisions = scm.revisions('', identifier_to, identifier_from, :with_paths => true)
+ transaction do
+ revisions.reverse_each do |revision|
+ changeset = Changeset.create(:repository => self,
+ :revision => revision.identifier,
+ :committer => revision.author,
+ :committed_on => revision.time,
+ :scmid => revision.scmid,
+ :comments => revision.message)
+
+ revision.paths.each do |change|
+ Change.create(:changeset => changeset,
+ :action => change[:action],
+ :path => change[:path],
+ :revision => change[:revision])
+ end
+ end
+ end unless revisions.nil?
+ identifier_from = identifier_to + 1
+ end
+ end
+ end
+ end
+end
diff --git a/app/views/repositories/revisions.rhtml b/app/views/repositories/revisions.rhtml
index 882d5ea4f..2a45fc2ef 100644
--- a/app/views/repositories/revisions.rhtml
+++ b/app/views/repositories/revisions.rhtml
@@ -1,7 +1,7 @@
<div class="contextual">
-<% form_tag do %>
-<p><%= l(:label_revision) %>: <%= text_field_tag 'rev', @rev, :size => 5 %>
-<%= submit_tag 'OK' %></p>
+<% form_tag({:action => 'revision', :id => @project}) do %>
+<%= l(:label_revision) %>: <%= text_field_tag 'rev', @rev, :size => 5 %>
+<%= submit_tag 'OK' %>
<% end %>
</div>
diff --git a/lib/redmine.rb b/lib/redmine.rb
index b74f00aec..32239ce59 100644
--- a/lib/redmine.rb
+++ b/lib/redmine.rb
@@ -10,7 +10,7 @@ rescue LoadError
# RMagick is not available
end
-REDMINE_SUPPORTED_SCM = %w( Subversion Darcs Mercurial Cvs )
+REDMINE_SUPPORTED_SCM = %w( Subversion Darcs Mercurial Cvs Bazaar )
# Permissions
Redmine::AccessControl.map do |map|
diff --git a/lib/redmine/scm/adapters/bazaar_adapter.rb b/lib/redmine/scm/adapters/bazaar_adapter.rb
new file mode 100644
index 000000000..ec9fd741c
--- /dev/null
+++ b/lib/redmine/scm/adapters/bazaar_adapter.rb
@@ -0,0 +1,204 @@
+# 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 BazaarAdapter < AbstractAdapter
+
+ # Bazaar executable name
+ BZR_BIN = "bzr"
+
+ # Get info about the repository
+ def info
+ cmd = "#{BZR_BIN} revno #{target('')}"
+ info = nil
+ shellout(cmd) do |io|
+ if io.read =~ %r{^(\d+)$}
+ info = Info.new({:root_url => url,
+ :lastrev => Revision.new({
+ :identifier => $1
+ })
+ })
+ end
+ end
+ return nil if $? && $?.exitstatus != 0
+ info
+ rescue Errno::ENOENT => e
+ return nil
+ end
+
+ # Returns the entry identified by path and revision identifier
+ # or nil if entry doesn't exist in the repository
+ def entry(path=nil, identifier=nil)
+ path ||= ''
+ parts = path.split(%r{[\/\\]}).select {|p| !p.blank?}
+ if parts.size > 0
+ parent = parts[0..-2].join('/')
+ entries = entries(parent, identifier)
+ entries ? entries.detect {|e| e.name == parts.last} : nil
+ end
+ 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 << " -r#{identifier.to_i}" if identifier && identifier.to_i > 0
+ cmd << " #{target(path)}"
+ shellout(cmd) do |io|
+ prefix = "#{url}/#{path}".gsub('\\', '/')
+ logger.debug "PREFIX: #{prefix}"
+ re = %r{^V\s+#{Regexp.escape(prefix)}(\/?)([^\/]+)(\/?)\s+(\S+)$}
+ io.each_line do |line|
+ next unless line =~ re
+ entries << Entry.new({:name => $2.strip,
+ :path => ((path.empty? ? "" : "#{path}/") + $2.strip),
+ :kind => ($3.blank? ? 'file' : 'dir'),
+ :size => nil,
+ :lastrev => Revision.new(:revision => $4.strip)
+ })
+ end
+ end
+ return nil if $? && $?.exitstatus != 0
+ logger.debug("Found #{entries.size} entries in the repository for #{target(path)}") if logger && logger.debug?
+ entries.sort_by_name
+ rescue Errno::ENOENT => e
+ raise CommandFailed
+ end
+
+ def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
+ path ||= ''
+ identifier_from = 'last:1' unless identifier_from and identifier_from.to_i > 0
+ identifier_to = 1 unless identifier_to and identifier_to.to_i > 0
+ revisions = Revisions.new
+ cmd = "#{BZR_BIN} log -v --show-ids -r#{identifier_to.to_i}..#{identifier_from} #{target(path)}"
+ shellout(cmd) do |io|
+ revision = nil
+ parsing = nil
+ io.each_line do |line|
+ if line =~ /^----/
+ revisions << revision if revision
+ revision = Revision.new(:paths => [], :message => '')
+ parsing = nil
+ else
+ next unless revision
+
+ if line =~ /^revno: (\d+)$/
+ revision.identifier = $1.to_i
+ elsif line =~ /^committer: (.+)$/
+ revision.author = $1.strip
+ elsif line =~ /^revision-id:(.+)$/
+ revision.scmid = $1.strip
+ elsif line =~ /^timestamp: (.+)$/
+ revision.time = Time.parse($1).localtime
+ elsif line =~ /^(message|added|modified|removed|renamed):/
+ parsing = $1
+ elsif line =~ /^ (.+)$/
+ if parsing == 'message'
+ revision.message << "#{$1}\n"
+ else
+ if $1 =~ /^(.*)\s+(\S+)$/
+ path = $1.strip
+ revid = $2
+ case parsing
+ when 'added'
+ revision.paths << {:action => 'A', :path => "/#{path}", :revision => revid}
+ when 'modified'
+ revision.paths << {:action => 'M', :path => "/#{path}", :revision => revid}
+ when 'removed'
+ revision.paths << {:action => 'D', :path => "/#{path}", :revision => revid}
+ when 'renamed'
+ new_path = path.split('=>').last
+ revision.paths << {:action => 'M', :path => "/#{new_path.strip}", :revision => revid} if new_path
+ end
+ end
+ end
+ else
+ parsing = nil
+ end
+ end
+ end
+ revisions << revision if revision
+ end
+ return nil if $? && $?.exitstatus != 0
+ revisions
+ rescue Errno::ENOENT => e
+ raise CommandFailed
+ end
+
+ def diff(path, identifier_from, identifier_to=nil, type="inline")
+ path ||= ''
+ if identifier_to
+ identifier_to = identifier_to.to_i
+ else
+ identifier_to = identifier_from.to_i - 1
+ end
+ cmd = "#{BZR_BIN} diff -r#{identifier_to}..#{identifier_from} #{target(path)}"
+ diff = []
+ shellout(cmd) do |io|
+ io.each_line do |line|
+ diff << line
+ end
+ end
+ #return nil if $? && $?.exitstatus != 0
+ DiffTableList.new diff, type
+ rescue Errno::ENOENT => e
+ raise CommandFailed
+ end
+
+ def cat(path, identifier=nil)
+ cmd = "#{BZR_BIN} cat"
+ cmd << " -r#{identifier.to_i}" if identifier && identifier.to_i > 0
+ cmd << " #{target(path)}"
+ cat = nil
+ shellout(cmd) do |io|
+ io.binmode
+ cat = io.read
+ end
+ return nil if $? && $?.exitstatus != 0
+ cat
+ rescue Errno::ENOENT => e
+ raise CommandFailed
+ end
+
+ def annotate(path, identifier=nil)
+ cmd = "#{BZR_BIN} annotate --all"
+ cmd << " -r#{identifier.to_i}" if identifier && identifier.to_i > 0
+ cmd << " #{target(path)}"
+ blame = Annotate.new
+ shellout(cmd) do |io|
+ author = nil
+ identifier = nil
+ io.each_line do |line|
+ next unless line =~ %r{^(\d+) ([^|]+)\| (.*)$}
+ blame.add_line($3.rstrip, Revision.new(:identifier => $1.to_i, :author => $2.strip))
+ end
+ end
+ return nil if $? && $?.exitstatus != 0
+ blame
+ rescue Errno::ENOENT => e
+ raise CommandFailed
+ end
+ end
+ end
+ end
+end