Adds support for multiple repositories per project (#779).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@8650 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
parent
d996cc0584
commit
1bd5e58c84
|
@ -28,7 +28,6 @@ class RepositoriesController < ApplicationController
|
|||
default_search_scope :changesets
|
||||
|
||||
before_filter :find_project_by_project_id, :only => [:new, :create]
|
||||
before_filter :check_repository_uniqueness, :only => [:new, :create]
|
||||
before_filter :find_repository, :only => [:edit, :update, :destroy, :committers]
|
||||
before_filter :find_project_repository, :except => [:new, :create, :edit, :update, :destroy, :committers]
|
||||
before_filter :authorize
|
||||
|
@ -39,6 +38,7 @@ class RepositoriesController < ApplicationController
|
|||
def new
|
||||
scm = params[:repository_scm] || Redmine::Scm::Base.all.first
|
||||
@repository = Repository.factory(scm)
|
||||
@repository.is_default = @project.repository.nil?
|
||||
@repository.project = @project
|
||||
render :layout => !request.xhr?
|
||||
end
|
||||
|
@ -97,6 +97,7 @@ class RepositoriesController < ApplicationController
|
|||
(show_error_not_found; return) unless @entries
|
||||
@changesets = @repository.latest_changesets(@path, @rev)
|
||||
@properties = @repository.properties(@path, @rev)
|
||||
@repositories = @project.repositories
|
||||
render :action => 'show'
|
||||
end
|
||||
end
|
||||
|
@ -255,18 +256,15 @@ class RepositoriesController < ApplicationController
|
|||
render_404
|
||||
end
|
||||
|
||||
# TODO: remove it when multiple SCM support is added
|
||||
def check_repository_uniqueness
|
||||
if @project.repository
|
||||
redirect_to settings_project_path(@project, :tab => 'repositories')
|
||||
end
|
||||
end
|
||||
|
||||
REV_PARAM_RE = %r{\A[a-f0-9]*\Z}i
|
||||
|
||||
def find_project_repository
|
||||
@project = Project.find(params[:id])
|
||||
if params[:repository_id].present?
|
||||
@repository = @project.repositories.find_by_identifier_param(params[:repository_id])
|
||||
else
|
||||
@repository = @project.repository
|
||||
end
|
||||
(render_404; return false) unless @repository
|
||||
@path = params[:path].join('/') unless params[:path].nil?
|
||||
@path ||= ''
|
||||
|
|
|
@ -19,7 +19,7 @@ class SysController < ActionController::Base
|
|||
before_filter :check_enabled
|
||||
|
||||
def projects
|
||||
p = Project.active.has_module(:repository).find(:all, :include => :repository, :order => 'identifier')
|
||||
p = Project.active.has_module(:repository).find(:all, :include => :repository, :order => "#{Project.table_name}.identifier")
|
||||
# extra_info attribute from repository breaks activeresource client
|
||||
render :xml => p.to_xml(:only => [:id, :identifier, :name, :is_public, :status], :include => {:repository => {:only => [:id, :url]}})
|
||||
end
|
||||
|
@ -44,11 +44,11 @@ class SysController < ActionController::Base
|
|||
if params[:id]
|
||||
projects << Project.active.has_module(:repository).find(params[:id])
|
||||
else
|
||||
projects = Project.active.has_module(:repository).find(:all, :include => :repository)
|
||||
projects = Project.active.has_module(:repository).all
|
||||
end
|
||||
projects.each do |project|
|
||||
if project.repository
|
||||
project.repository.fetch_changesets
|
||||
project.repositories.each do |repository|
|
||||
repository.fetch_changesets
|
||||
end
|
||||
end
|
||||
render :nothing => true, :status => 200
|
||||
|
|
|
@ -106,12 +106,15 @@ module ApplicationHelper
|
|||
# Generates a link to a SCM revision
|
||||
# Options:
|
||||
# * :text - Link text (default to the formatted revision)
|
||||
def link_to_revision(revision, project, options={})
|
||||
def link_to_revision(revision, repository, options={})
|
||||
if repository.is_a?(Project)
|
||||
repository = repository.repository
|
||||
end
|
||||
text = options.delete(:text) || format_revision(revision)
|
||||
rev = revision.respond_to?(:identifier) ? revision.identifier : revision
|
||||
link_to(
|
||||
h(text),
|
||||
{:controller => 'repositories', :action => 'revision', :id => project, :rev => rev},
|
||||
{:controller => 'repositories', :action => 'revision', :id => repository.project, :repository_id => repository.identifier_param, :rev => rev},
|
||||
:title => l(:label_revision_id, format_revision(revision))
|
||||
)
|
||||
end
|
||||
|
|
|
@ -92,6 +92,7 @@ module RepositoriesHelper
|
|||
text = link_to(h(text), :controller => 'repositories',
|
||||
:action => 'show',
|
||||
:id => @project,
|
||||
:repository_id => @repository.identifier_param,
|
||||
:path => path_param,
|
||||
:rev => @changeset.identifier)
|
||||
output << "<li class='#{style}'>#{text}"
|
||||
|
@ -103,12 +104,14 @@ module RepositoriesHelper
|
|||
text = link_to(h(text), :controller => 'repositories',
|
||||
:action => 'entry',
|
||||
:id => @project,
|
||||
:repository_id => @repository.identifier_param,
|
||||
:path => path_param,
|
||||
:rev => @changeset.identifier) unless c.action == 'D'
|
||||
text << " - #{h(c.revision)}" unless c.revision.blank?
|
||||
text << ' ('.html_safe + link_to(l(:label_diff), :controller => 'repositories',
|
||||
:action => 'diff',
|
||||
:id => @project,
|
||||
:repository_id => @repository.identifier_param,
|
||||
:path => path_param,
|
||||
:rev => @changeset.identifier) + ') '.html_safe if c.action == 'M'
|
||||
text << ' '.html_safe + content_tag('span', h(c.from_path), :class => 'copied-from') unless c.from_path.blank?
|
||||
|
|
|
@ -46,7 +46,8 @@ class Project < ActiveRecord::Base
|
|||
has_many :news, :dependent => :destroy, :include => :author
|
||||
has_many :issue_categories, :dependent => :delete_all, :order => "#{IssueCategory.table_name}.name"
|
||||
has_many :boards, :dependent => :destroy, :order => "position ASC"
|
||||
has_one :repository, :dependent => :destroy
|
||||
has_one :repository, :conditions => ["is_default = ?", true]
|
||||
has_many :repositories, :dependent => :destroy
|
||||
has_many :changesets, :through => :repository
|
||||
has_one :wiki, :dependent => :destroy
|
||||
# Custom field for the project issues
|
||||
|
|
|
@ -26,11 +26,19 @@ class Repository < ActiveRecord::Base
|
|||
|
||||
serialize :extra_info
|
||||
|
||||
before_save :check_default
|
||||
|
||||
# Raw SQL to delete changesets and changes in the database
|
||||
# has_many :changesets, :dependent => :destroy is too slow for big repositories
|
||||
before_destroy :clear_changesets
|
||||
|
||||
validates_length_of :password, :maximum => 255, :allow_nil => true
|
||||
validates_length_of :identifier, :maximum => 255, :allow_blank => true
|
||||
validates_presence_of :identifier, :unless => Proc.new { |r| r.is_default? || r.set_as_default? }
|
||||
validates_uniqueness_of :identifier, :scope => :project_id, :allow_blank => true
|
||||
validates_exclusion_of :identifier, :in => %w(show entry raw changes annotate diff show stats graph)
|
||||
# donwcase letters, digits, dashes but not digits only
|
||||
validates_format_of :identifier, :with => /^(?!\d+$)[a-z0-9\-]*$/, :allow_blank => true
|
||||
# Checks if the SCM is enabled when creating a repository
|
||||
validate :repo_create_validation, :on => :create
|
||||
|
||||
|
@ -65,8 +73,10 @@ class Repository < ActiveRecord::Base
|
|||
end
|
||||
|
||||
send :attributes_without_extra_info=, p, guard_protected_attributes
|
||||
if p_extra.keys.any?
|
||||
merge_extra_info(p_extra)
|
||||
end
|
||||
end
|
||||
|
||||
# Removes leading and trailing whitespace
|
||||
def url=(arg)
|
||||
|
@ -101,6 +111,44 @@ class Repository < ActiveRecord::Base
|
|||
self.class.scm_name
|
||||
end
|
||||
|
||||
def name
|
||||
if is_default?
|
||||
l(:field_repository_is_default)
|
||||
elsif identifier.present?
|
||||
identifier
|
||||
else
|
||||
scm_name
|
||||
end
|
||||
end
|
||||
|
||||
def identifier_param
|
||||
if is_default?
|
||||
nil
|
||||
elsif identifier.present?
|
||||
identifier
|
||||
else
|
||||
id.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def <=>(repository)
|
||||
if is_default?
|
||||
-1
|
||||
elsif repository.is_default?
|
||||
1
|
||||
else
|
||||
identifier <=> repository.identifier
|
||||
end
|
||||
end
|
||||
|
||||
def self.find_by_identifier_param(param)
|
||||
if param.to_s =~ /^\d+$/
|
||||
find_by_id(param)
|
||||
else
|
||||
find_by_identifier(param)
|
||||
end
|
||||
end
|
||||
|
||||
def merge_extra_info(arg)
|
||||
h = extra_info || {}
|
||||
return h if arg.nil?
|
||||
|
@ -269,10 +317,10 @@ class Repository < ActiveRecord::Base
|
|||
# Can be called periodically by an external script
|
||||
# eg. ruby script/runner "Repository.fetch_changesets"
|
||||
def self.fetch_changesets
|
||||
Project.active.has_module(:repository).find(:all, :include => :repository).each do |project|
|
||||
if project.repository
|
||||
Project.active.has_module(:repository).all.each do |project|
|
||||
project.repositories.each do |repository|
|
||||
begin
|
||||
project.repository.fetch_changesets
|
||||
repository.fetch_changesets
|
||||
rescue Redmine::Scm::Adapters::CommandFailed => e
|
||||
logger.error "scm: error during fetching changesets: #{e.message}"
|
||||
end
|
||||
|
@ -334,6 +382,21 @@ class Repository < ActiveRecord::Base
|
|||
ret
|
||||
end
|
||||
|
||||
def set_as_default?
|
||||
new_record? && project && !Repository.first(:conditions => {:project_id => project.id})
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def check_default
|
||||
if !is_default? && set_as_default?
|
||||
self.is_default = true
|
||||
end
|
||||
if is_default? && is_default_changed?
|
||||
Repository.update_all(["is_default = ?", false], ["project_id = ?", project_id])
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def clear_changesets
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<% changesets.each do |changeset| %>
|
||||
<div class="changeset <%= cycle('odd', 'even') %>">
|
||||
<p><%= link_to_revision(changeset, changeset.project,
|
||||
<p><%= link_to_revision(changeset, changeset.repository,
|
||||
:text => "#{l(:label_revision)} #{changeset.format_identifier}") %><br />
|
||||
<span class="author"><%= authoring(changeset.committed_on, changeset.author) %></span></p>
|
||||
<div class="wiki">
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
<% if @project.repository %>
|
||||
<% if @project.repositories.any? %>
|
||||
<table class="list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><%= l(:label_scm) %></th>
|
||||
<th><%= l(:field_identifier) %></th>
|
||||
<th><%= l(:field_repository_is_default) %></th>
|
||||
<th><%= l(:label_repository) %></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% repository = @project.repository %>
|
||||
<% @project.repositories.each do |repository| %>
|
||||
<tr class="<%= cycle 'odd', 'even' %>">
|
||||
<td><%=h repository.scm_name %></td>
|
||||
<td><%=h repository.identifier %></td>
|
||||
<td align="center"><%= checked_image repository.is_default? %></td>
|
||||
<td><%=h repository.url %></td>
|
||||
<td class="buttons">
|
||||
<% if User.current.allowed_to?(:manage_repository, @project) %>
|
||||
|
@ -25,12 +29,13 @@
|
|||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
<% else %>
|
||||
<p class="nodata"><%= l(:label_no_data) %></p>
|
||||
<% end %>
|
||||
|
||||
<% if @project.repository.nil? && User.current.allowed_to?(:manage_repository, @project) %>
|
||||
<% if User.current.allowed_to?(:manage_repository, @project) %>
|
||||
<p><%= link_to l(:label_repository_new), new_project_repository_path(@project), :class => 'icon icon-add' %></p>
|
||||
<% end %>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<%= link_to 'root', :action => 'show', :id => @project, :path => '', :rev => @rev %>
|
||||
<%= link_to 'root', :action => 'show', :id => @project, :repository_id => @repository.identifier_param, :path => '', :rev => @rev %>
|
||||
<%
|
||||
dirs = path.split('/')
|
||||
if 'file' == kind
|
||||
|
@ -10,12 +10,12 @@ dirs.each do |dir|
|
|||
link_path << '/' unless link_path.empty?
|
||||
link_path << "#{dir}"
|
||||
%>
|
||||
/ <%= link_to h(dir), :action => 'show', :id => @project,
|
||||
/ <%= link_to h(dir), :action => 'show', :id => @project, :repository_id => @repository.identifier_param,
|
||||
:path => to_path_param(link_path), :rev => @rev %>
|
||||
<% end %>
|
||||
<% if filename %>
|
||||
/ <%= link_to h(filename),
|
||||
:action => 'changes', :id => @project,
|
||||
:action => 'changes', :id => @project, :repository_id => @repository.identifier_param,
|
||||
:path => to_path_param("#{link_path}/#{filename}"), :rev => @rev %>
|
||||
<% end %>
|
||||
<%
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
:url => {
|
||||
:action => 'show',
|
||||
:id => @project,
|
||||
:repository_id => @repository.identifier_param,
|
||||
:path => to_path_param(ent_path),
|
||||
:rev => @rev,
|
||||
:depth => (depth + 1),
|
||||
|
@ -24,13 +25,13 @@
|
|||
) %>"> </span>
|
||||
<% end %>
|
||||
<%= link_to h(ent_name),
|
||||
{:action => (entry.is_dir? ? 'show' : 'changes'), :id => @project, :path => to_path_param(ent_path), :rev => @rev},
|
||||
{:action => (entry.is_dir? ? 'show' : 'changes'), :id => @project, :repository_id => @repository.identifier_param, :path => to_path_param(ent_path), :rev => @rev},
|
||||
:class => (entry.is_dir? ? 'icon icon-folder' : "icon icon-file #{Redmine::MimeType.css_class_of(ent_name)}")%>
|
||||
</td>
|
||||
<td class="size"><%= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %></td>
|
||||
<% changeset = @project.repository.find_changeset_by_name(entry.lastrev.identifier) if entry.lastrev && entry.lastrev.identifier %>
|
||||
<% if @repository.report_last_commit %>
|
||||
<td class="revision"><%= link_to_revision(changeset, @project) if changeset %></td>
|
||||
<td class="revision"><%= link_to_revision(changeset, @repository) if changeset %></td>
|
||||
<td class="age"><%= distance_of_time_in_words(entry.lastrev.time, Time.now) if entry.lastrev && entry.lastrev.time %></td>
|
||||
<td class="author"><%= changeset.nil? ? h(Redmine::CodesetUtil.replace_invalid_utf8(entry.lastrev.author.to_s.split('<').first)) : h(changeset.author) if entry.lastrev %></td>
|
||||
<td class="comments"><%=h truncate(changeset.comments, :length => 50) unless changeset.nil? %></td>
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
<% end %>
|
||||
</p>
|
||||
|
||||
<p><%= f.text_field :identifier %></p>
|
||||
<p><%= f.check_box :is_default, :label => :field_repository_is_default %></p>
|
||||
|
||||
<% button_disabled = true %>
|
||||
<% if @repository %>
|
||||
<% button_disabled = ! @repository.class.scm_available %>
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
<% if @entry && @entry.kind == 'file' %>
|
||||
|
||||
<p>
|
||||
<%= link_to_if action_name != 'changes', l(:label_history), {:action => 'changes', :id => @project, :path => to_path_param(@path), :rev => @rev } %> |
|
||||
<%= link_to_if action_name != 'changes', l(:label_history), {:action => 'changes', :id => @project, :repository_id => @repository.identifier_param, :path => to_path_param(@path), :rev => @rev } %> |
|
||||
<% if @repository.supports_cat? %>
|
||||
<%= link_to_if action_name != 'entry', l(:button_view), {:action => 'entry', :id => @project, :path => to_path_param(@path), :rev => @rev } %> |
|
||||
<%= link_to_if action_name != 'entry', l(:button_view), {:action => 'entry', :id => @project, :repository_id => @repository.identifier_param, :path => to_path_param(@path), :rev => @rev } %> |
|
||||
<% end %>
|
||||
<% if @repository.supports_annotate? %>
|
||||
<%= link_to_if action_name != 'annotate', l(:button_annotate), {:action => 'annotate', :id => @project, :path => to_path_param(@path), :rev => @rev } %> |
|
||||
<%= link_to_if action_name != 'annotate', l(:button_annotate), {:action => 'annotate', :id => @project, :repository_id => @repository.identifier_param, :path => to_path_param(@path), :rev => @rev } %> |
|
||||
<% end %>
|
||||
<%= link_to(l(:button_download), {:action => 'entry', :id => @project, :path => to_path_param(@path), :rev => @rev, :format => 'raw' }) if @repository.supports_cat? %>
|
||||
<%= link_to(l(:button_download), {:action => 'entry', :id => @project, :repository_id => @repository.identifier_param, :path => to_path_param(@path), :rev => @rev, :format => 'raw' }) if @repository.supports_cat? %>
|
||||
<%= "(#{number_to_human_size(@entry.size)})" if @entry.size %>
|
||||
</p>
|
||||
|
||||
|
|
|
@ -3,11 +3,12 @@
|
|||
<% end %>
|
||||
|
||||
<%= link_to l(:label_statistics),
|
||||
{:action => 'stats', :id => @project},
|
||||
{:action => 'stats', :id => @project, :repository_id => @repository.identifier_param},
|
||||
:class => 'icon icon-stats' %>
|
||||
|
||||
<% form_tag({:action => controller.action_name,
|
||||
:id => @project,
|
||||
:repository_id => @repository.identifier_param,
|
||||
:path => to_path_param(@path),
|
||||
:rev => ''},
|
||||
{:method => :get, :id => 'revision_selector'}) do -%>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<% show_revision_graph = ( @repository.supports_revision_graph? && path.blank? ) %>
|
||||
<% form_tag({:controller => 'repositories', :action => 'diff', :id => @project, :path => to_path_param(path)}, :method => :get) do %>
|
||||
<% form_tag({:controller => 'repositories', :action => 'diff', :id => @project, :repository_id => @repository.identifier_param, :path => to_path_param(path)}, :method => :get) do %>
|
||||
<table class="list changesets">
|
||||
<thead><tr>
|
||||
<% if show_revision_graph %>
|
||||
|
@ -23,6 +23,7 @@
|
|||
<% href_base = Proc.new {|x| url_for(:controller => 'repositories',
|
||||
:action => 'revision',
|
||||
:id => project,
|
||||
:repository_id => @repository.identifier_param,
|
||||
:rev => x) } %>
|
||||
<%= render :partial => 'revision_graph',
|
||||
:locals => {
|
||||
|
@ -35,7 +36,7 @@
|
|||
</td>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<td class="id"><%= link_to_revision(changeset, project) %></td>
|
||||
<td class="id"><%= link_to_revision(changeset, @repository) %></td>
|
||||
<td class="checkbox"><%= radio_button_tag('rev', changeset.identifier, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('cbto-#{line_num+1}').checked=true;") if show_diff && (line_num < revisions.size) %></td>
|
||||
<td class="checkbox"><%= radio_button_tag('rev_to', changeset.identifier, (line_num==2), :id => "cbto-#{line_num}", :onclick => "if ($('cb-#{line_num}').checked==true) {$('cb-#{line_num-1}').checked=true;}") if show_diff && (line_num > 1) %></td>
|
||||
<td class="committed_on"><%= format_time(changeset.committed_on) %></td>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<tr class="bloc-<%= revision.nil? ? 0 : colors[revision.identifier || revision.revision] %>">
|
||||
<th class="line-num" id="L<%= line_num %>"><a href="#L<%= line_num %>"><%= line_num %></a></th>
|
||||
<td class="revision">
|
||||
<%= (revision.identifier ? link_to_revision(revision, @project) : format_revision(revision)) if revision %></td>
|
||||
<%= (revision.identifier ? link_to_revision(revision, @repository) : format_revision(revision)) if revision %></td>
|
||||
<td class="author"><%= h(revision.author.to_s.split('<').first) if revision %></td>
|
||||
<td class="line-code"><pre><%= line %></pre></td>
|
||||
</tr>
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<div class="contextual">
|
||||
«
|
||||
<% unless @changeset.previous.nil? -%>
|
||||
<%= link_to_revision(@changeset.previous, @project, :text => l(:label_previous)) %>
|
||||
<%= link_to_revision(@changeset.previous, @repository, :text => l(:label_previous)) %>
|
||||
<% else -%>
|
||||
<%= l(:label_previous) %>
|
||||
<% end -%>
|
||||
|
|
||||
<% unless @changeset.next.nil? -%>
|
||||
<%= link_to_revision(@changeset.next, @project, :text => l(:label_next)) %>
|
||||
<%= link_to_revision(@changeset.next, @repository, :text => l(:label_next)) %>
|
||||
<% else -%>
|
||||
<%= l(:label_next) %>
|
||||
<% end -%>
|
||||
|
@ -16,6 +16,7 @@
|
|||
<% form_tag({:controller => 'repositories',
|
||||
:action => 'revision',
|
||||
:id => @project,
|
||||
:repository_id => @repository.identifier_param,
|
||||
:rev => nil},
|
||||
:method => :get) do %>
|
||||
<%= text_field_tag 'rev', @rev, :size => 8 %>
|
||||
|
@ -37,7 +38,7 @@
|
|||
<td><%= l(:label_parent_revision) %></td>
|
||||
<td>
|
||||
<%= @changeset.parents.collect{
|
||||
|p| link_to_revision(p, @project, :text => format_revision(p))
|
||||
|p| link_to_revision(p, @repository, :text => format_revision(p))
|
||||
}.join(", ").html_safe %>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -47,7 +48,7 @@
|
|||
<td><%= l(:label_child_revision) %></td>
|
||||
<td>
|
||||
<%= @changeset.children.collect{
|
||||
|p| link_to_revision(p, @project, :text => format_revision(p))
|
||||
|p| link_to_revision(p, @repository, :text => format_revision(p))
|
||||
}.join(", ").html_safe %>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -85,6 +86,7 @@
|
|||
<p><%= link_to(l(:label_view_diff),
|
||||
:action => 'diff',
|
||||
:id => @project,
|
||||
:repository_id => @repository.identifier_param,
|
||||
:path => "",
|
||||
:rev => @changeset.identifier) if @changeset.changes.any? %></p>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<div class="contextual">
|
||||
<% form_tag({:action => 'revision', :id => @project}) do %>
|
||||
<% form_tag({:action => 'revision', :id => @project, :repository_id => @repository.identifier_param}) do %>
|
||||
<%= l(:label_revision) %>: <%= text_field_tag 'rev', @rev, :size => 8 %>
|
||||
<%= submit_tag 'OK' %>
|
||||
<% end %>
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
sep = ''
|
||||
%>
|
||||
<% if @repository.supports_all_revisions? && @path.blank? %>
|
||||
<%= link_to l(:label_view_all_revisions), :action => 'revisions', :id => @project %>
|
||||
<%= link_to l(:label_view_all_revisions), :action => 'revisions', :id => @project, :repository_id => @repository.identifier_param %>
|
||||
<% sep = '|' %>
|
||||
<% end %>
|
||||
<%
|
||||
|
@ -38,6 +38,7 @@
|
|||
:action => 'changes',
|
||||
:path => to_path_param(@path),
|
||||
:id => @project,
|
||||
:repository_id => @repository.identifier_param,
|
||||
:rev => @rev
|
||||
%>
|
||||
<% end %>
|
||||
|
@ -52,11 +53,22 @@
|
|||
<% end %>
|
||||
|
||||
<% other_formats_links do |f| %>
|
||||
<%= f.link_to 'Atom', :url => {:action => 'revisions', :id => @project, :key => User.current.rss_key} %>
|
||||
<%= f.link_to 'Atom', :url => {:action => 'revisions', :id => @project, :repository_id => @repository.identifier_param, :key => User.current.rss_key} %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% if @repositories.size > 1 %>
|
||||
<% content_for :sidebar do %>
|
||||
<h3><%= l(:label_repository_plural) %></h3>
|
||||
<%= @repositories.sort.collect {|repo|
|
||||
link_to h(repo.name),
|
||||
{:controller => 'repositories', :action => 'show', :id => @project, :repository_id => repo.identifier_param, :rev => nil, :path => nil},
|
||||
:class => 'repository' + (repo == @repository ? ' selected' : '')
|
||||
}.join('<br />').html_safe %></p>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% content_for :header_tags do %>
|
||||
<%= stylesheet_link_tag "scm" %>
|
||||
<% end %>
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<h2><%= l(:label_statistics) %></h2>
|
||||
|
||||
<p>
|
||||
<%= tag("embed", :width => 800, :height => 300, :type => "image/svg+xml", :src => url_for(:controller => 'repositories', :action => 'graph', :id => @project, :graph => "commits_per_month")) %>
|
||||
<%= tag("embed", :width => 800, :height => 300, :type => "image/svg+xml", :src => url_for(:controller => 'repositories', :action => 'graph', :id => @project, :repository_id => @repository.identifier_param, :graph => "commits_per_month")) %>
|
||||
</p>
|
||||
<p>
|
||||
<%= tag("embed", :width => 800, :height => 400, :type => "image/svg+xml", :src => url_for(:controller => 'repositories', :action => 'graph', :id => @project, :graph => "commits_per_author")) %>
|
||||
<%= tag("embed", :width => 800, :height => 400, :type => "image/svg+xml", :src => url_for(:controller => 'repositories', :action => 'graph', :id => @project, :repository_id => @repository.identifier_param, :graph => "commits_per_author")) %>
|
||||
</p>
|
||||
|
||||
<p><%= link_to l(:button_back), :action => 'show', :id => @project %></p>
|
||||
|
|
|
@ -318,6 +318,7 @@ en:
|
|||
field_root_directory: Root directory
|
||||
field_cvsroot: CVSROOT
|
||||
field_cvs_module: Module
|
||||
field_repository_is_default: Main repository
|
||||
|
||||
setting_app_title: Application title
|
||||
setting_app_subtitle: Application subtitle
|
||||
|
|
|
@ -317,6 +317,7 @@ fr:
|
|||
field_issues_visibility: Visibilité des demandes
|
||||
field_is_private: Privée
|
||||
field_commit_logs_encoding: Encodage des messages de commit
|
||||
field_repository_is_default: Dépôt principal
|
||||
|
||||
setting_app_title: Titre de l'application
|
||||
setting_app_subtitle: Sous-titre de l'application
|
||||
|
|
|
@ -235,7 +235,8 @@ ActionController::Routing::Routes.draw do |map|
|
|||
:action => 'show'
|
||||
repository_views.connect 'projects/:id/repository/statistics',
|
||||
:action => 'stats'
|
||||
|
||||
repository_views.connect 'projects/:id/repository/graph',
|
||||
:action => 'graph'
|
||||
repository_views.connect 'projects/:id/repository/revisions',
|
||||
:action => 'revisions'
|
||||
repository_views.connect 'projects/:id/repository/revisions.:format',
|
||||
|
@ -247,28 +248,39 @@ ActionController::Routing::Routes.draw do |map|
|
|||
repository_views.connect 'projects/:id/repository/revisions/:rev/diff.:format',
|
||||
:action => 'diff'
|
||||
repository_views.connect 'projects/:id/repository/revisions/:rev/raw/*path',
|
||||
:action => 'entry',
|
||||
:format => 'raw',
|
||||
:requirements => { :rev => /[a-z0-9\.\-_]+/ }
|
||||
:action => 'entry', :format => 'raw'
|
||||
repository_views.connect 'projects/:id/repository/revisions/:rev/:action/*path',
|
||||
:requirements => { :rev => /[a-z0-9\.\-_]+/ }
|
||||
|
||||
:requirements => { :action => /(browse|show|entry|changes|annotate|diff)/ }
|
||||
repository_views.connect 'projects/:id/repository/raw/*path',
|
||||
:action => 'entry', :format => 'raw'
|
||||
repository_views.connect 'projects/:id/repository/browse/*path',
|
||||
:action => 'browse'
|
||||
repository_views.connect 'projects/:id/repository/entry/*path',
|
||||
:action => 'entry'
|
||||
repository_views.connect 'projects/:id/repository/changes/*path',
|
||||
:action => 'changes'
|
||||
repository_views.connect 'projects/:id/repository/annotate/*path',
|
||||
:action => 'annotate'
|
||||
repository_views.connect 'projects/:id/repository/diff/*path',
|
||||
:action => 'diff'
|
||||
repository_views.connect 'projects/:id/repository/show/*path',
|
||||
:action => 'show'
|
||||
repository_views.connect 'projects/:id/repository/graph',
|
||||
repository_views.connect 'projects/:id/repository/:action/*path',
|
||||
:requirements => { :action => /(browse|show|entry|changes|annotate|diff)/ }
|
||||
|
||||
# Same routes with a repository_id
|
||||
repository_views.connect 'projects/:id/repository/:repository_id/statistics',
|
||||
:action => 'stats'
|
||||
repository_views.connect 'projects/:id/repository/:repository_id/graph',
|
||||
:action => 'graph'
|
||||
repository_views.connect 'projects/:id/repository/:repository_id/revisions',
|
||||
:action => 'revisions'
|
||||
repository_views.connect 'projects/:id/repository/:repository_id/revisions.:format',
|
||||
:action => 'revisions'
|
||||
repository_views.connect 'projects/:id/repository/:repository_id/revisions/:rev',
|
||||
:action => 'revision'
|
||||
repository_views.connect 'projects/:id/repository/:repository_id/revisions/:rev/diff',
|
||||
:action => 'diff'
|
||||
repository_views.connect 'projects/:id/repository/:repository_id/revisions/:rev/diff.:format',
|
||||
:action => 'diff'
|
||||
repository_views.connect 'projects/:id/repository/:repository_id/revisions/:rev/raw/*path',
|
||||
:action => 'entry', :format => 'raw'
|
||||
repository_views.connect 'projects/:id/repository/:repository_id/revisions/:rev/:action/*path',
|
||||
:requirements => { :action => /(browse|show|entry|changes|annotate|diff)/ }
|
||||
repository_views.connect 'projects/:id/repository/:repository_id/raw/*path',
|
||||
:action => 'entry', :format => 'raw'
|
||||
repository_views.connect 'projects/:id/repository/:repository_id/:action/*path',
|
||||
:requirements => { :action => /(browse|show|entry|changes|annotate|diff)/ }
|
||||
repository_views.connect 'projects/:id/repository/:repository_id',
|
||||
:action => 'show'
|
||||
end
|
||||
|
||||
repositories.connect 'projects/:id/repository/revision',
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
class AddRepositoriesIdentifier < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column :repositories, :identifier, :string
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :repositories, :identifier
|
||||
end
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
class AddRepositoriesIsDefault < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column :repositories, :is_default, :boolean, :default => false
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :repositories, :is_default
|
||||
end
|
||||
end
|
|
@ -0,0 +1,14 @@
|
|||
class SetDefaultRepositories < ActiveRecord::Migration
|
||||
def self.up
|
||||
Repository.update_all(["is_default = ?", false])
|
||||
# Sets the last repository as default in case multiple repositories exist for the same project
|
||||
Repository.connection.select_values("SELECT r.id FROM #{Repository.table_name} r" +
|
||||
" WHERE r.id = (SELECT max(r1.id) FROM #{Repository.table_name} r1 WHERE r1.project_id = r.project_id)").each do |i|
|
||||
Repository.update_all(["is_default = ?", true], ["id = ?", i])
|
||||
end
|
||||
end
|
||||
|
||||
def self.down
|
||||
Repository.update_all(["is_default = ?", false])
|
||||
end
|
||||
end
|
|
@ -108,6 +108,7 @@ a:hover, a:active{ color: #c61a1a; text-decoration: underline;}
|
|||
a img{ border: 0; }
|
||||
|
||||
a.issue.closed, a.issue.closed:link, a.issue.closed:visited { color: #999; text-decoration: line-through; }
|
||||
a.repository.selected {font-weight:bold;}
|
||||
|
||||
/***** Tables *****/
|
||||
table.list { border: 1px solid #e4e4e4; border-collapse: collapse; width: 100%; margin-bottom: 4px; }
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class Repository < ActiveRecord::Base
|
||||
generator_for :type => 'Subversion'
|
||||
generator_for :url, :start => 'file:///test/svn'
|
||||
|
||||
generator_for :identifier, :start => 'repo1'
|
||||
end
|
||||
|
|
|
@ -7,6 +7,7 @@ repositories_001:
|
|||
password: ""
|
||||
login: ""
|
||||
type: Subversion
|
||||
is_default: true
|
||||
repositories_002:
|
||||
project_id: 2
|
||||
url: svn://localhost/test
|
||||
|
@ -15,3 +16,4 @@ repositories_002:
|
|||
password: ""
|
||||
login: ""
|
||||
type: Subversion
|
||||
is_default: true
|
||||
|
|
|
@ -43,19 +43,12 @@ class RepositoriesControllerTest < ActionController::TestCase
|
|||
assert_tag 'input', :attributes => {:name => 'repository[url]'}
|
||||
end
|
||||
|
||||
# TODO: remove it when multiple SCM support is added
|
||||
def test_new_with_existing_repository
|
||||
@request.session[:user_id] = 1
|
||||
get :new, :project_id => 'ecookbook'
|
||||
assert_response 302
|
||||
end
|
||||
|
||||
def test_create
|
||||
@request.session[:user_id] = 1
|
||||
assert_difference 'Repository.count' do
|
||||
post :create, :project_id => 'subproject1',
|
||||
:repository_scm => 'Subversion',
|
||||
:repository => {:url => 'file:///test'}
|
||||
:repository => {:url => 'file:///test', :is_default => '1', :identifier => ''}
|
||||
end
|
||||
assert_response 302
|
||||
repository = Repository.first(:order => 'id DESC')
|
||||
|
@ -113,9 +106,25 @@ class RepositoriesControllerTest < ActionController::TestCase
|
|||
get :revisions, :id => 1
|
||||
assert_response :success
|
||||
assert_template 'revisions'
|
||||
assert_equal Repository.find(10), assigns(:repository)
|
||||
assert_not_nil assigns(:changesets)
|
||||
end
|
||||
|
||||
def test_revisions_for_other_repository
|
||||
repository = Repository::Subversion.create!(:project_id => 1, :identifier => 'foo', :url => 'file:///foo')
|
||||
|
||||
get :revisions, :id => 1, :repository_id => 'foo'
|
||||
assert_response :success
|
||||
assert_template 'revisions'
|
||||
assert_equal repository, assigns(:repository)
|
||||
assert_not_nil assigns(:changesets)
|
||||
end
|
||||
|
||||
def test_revisions_for_invalid_repository
|
||||
get :revisions, :id => 1, :repository_id => 'foo'
|
||||
assert_response 404
|
||||
end
|
||||
|
||||
def test_revision
|
||||
get :revision, :id => 1, :rev => 1
|
||||
assert_response :success
|
||||
|
|
|
@ -70,6 +70,29 @@ class RoutingRepositoriesTest < ActionController::IntegrationTest
|
|||
:path => "/projects/redmine/repository/statistics" },
|
||||
{ :controller => 'repositories', :action => 'stats', :id => 'redmine' }
|
||||
)
|
||||
assert_routing(
|
||||
{ :method => 'get',
|
||||
:path => "/projects/redmine/repository/graph" },
|
||||
{ :controller => 'repositories', :action => 'graph', :id => 'redmine' }
|
||||
)
|
||||
end
|
||||
|
||||
def test_repositories_with_repository_id
|
||||
assert_routing(
|
||||
{ :method => 'get',
|
||||
:path => "/projects/redmine/repository/foo" },
|
||||
{ :controller => 'repositories', :action => 'show', :id => 'redmine', :repository_id => 'foo' }
|
||||
)
|
||||
assert_routing(
|
||||
{ :method => 'get',
|
||||
:path => "/projects/redmine/repository/foo/statistics" },
|
||||
{ :controller => 'repositories', :action => 'stats', :id => 'redmine', :repository_id => 'foo' }
|
||||
)
|
||||
assert_routing(
|
||||
{ :method => 'get',
|
||||
:path => "/projects/redmine/repository/foo/graph" },
|
||||
{ :controller => 'repositories', :action => 'graph', :id => 'redmine', :repository_id => 'foo' }
|
||||
)
|
||||
end
|
||||
|
||||
def test_repositories_revisions
|
||||
|
@ -153,6 +176,87 @@ class RoutingRepositoriesTest < ActionController::IntegrationTest
|
|||
)
|
||||
end
|
||||
|
||||
def test_repositories_revisions_with_repository_id
|
||||
empty_path_param = []
|
||||
assert_routing(
|
||||
{ :method => 'get',
|
||||
:path => "/projects/redmine/repository/foo/revisions" },
|
||||
{ :controller => 'repositories', :action => 'revisions', :id => 'redmine', :repository_id => 'foo' }
|
||||
)
|
||||
assert_routing(
|
||||
{ :method => 'get',
|
||||
:path => "/projects/redmine/repository/foo/revisions.atom" },
|
||||
{ :controller => 'repositories', :action => 'revisions', :id => 'redmine', :repository_id => 'foo',
|
||||
:format => 'atom' }
|
||||
)
|
||||
assert_routing(
|
||||
{ :method => 'get',
|
||||
:path => "/projects/redmine/repository/foo/revisions/2457" },
|
||||
{ :controller => 'repositories', :action => 'revision', :id => 'redmine', :repository_id => 'foo',
|
||||
:rev => '2457' }
|
||||
)
|
||||
assert_routing(
|
||||
{ :method => 'get',
|
||||
:path => "/projects/redmine/repository/foo/revisions/2457/show" },
|
||||
{ :controller => 'repositories', :action => 'show', :id => 'redmine', :repository_id => 'foo',
|
||||
:path => empty_path_param, :rev => '2457' }
|
||||
)
|
||||
assert_routing(
|
||||
{ :method => 'get',
|
||||
:path => "/projects/redmine/repository/foo/revisions/2457/show/#{@path_hash[:path]}" },
|
||||
{ :controller => 'repositories', :action => 'show', :id => 'redmine', :repository_id => 'foo',
|
||||
:path => @path_hash[:param] , :rev => '2457'}
|
||||
)
|
||||
assert_routing(
|
||||
{ :method => 'get',
|
||||
:path => "/projects/redmine/repository/foo/revisions/2457/changes" },
|
||||
{ :controller => 'repositories', :action => 'changes', :id => 'redmine', :repository_id => 'foo',
|
||||
:path => empty_path_param, :rev => '2457' }
|
||||
)
|
||||
assert_routing(
|
||||
{ :method => 'get',
|
||||
:path => "/projects/redmine/repository/foo/revisions/2457/changes/#{@path_hash[:path]}" },
|
||||
{ :controller => 'repositories', :action => 'changes', :id => 'redmine', :repository_id => 'foo',
|
||||
:path => @path_hash[:param] , :rev => '2457'}
|
||||
)
|
||||
assert_routing(
|
||||
{ :method => 'get',
|
||||
:path => "/projects/redmine/repository/foo/revisions/2457/diff" },
|
||||
{ :controller => 'repositories', :action => 'diff', :id => 'redmine', :repository_id => 'foo',
|
||||
:rev => '2457' }
|
||||
)
|
||||
assert_routing(
|
||||
{ :method => 'get',
|
||||
:path => "/projects/redmine/repository/foo/revisions/2457/diff.diff" },
|
||||
{ :controller => 'repositories', :action => 'diff', :id => 'redmine', :repository_id => 'foo',
|
||||
:rev => '2457', :format => 'diff' }
|
||||
)
|
||||
assert_routing(
|
||||
{ :method => 'get',
|
||||
:path => "/projects/redmine/repository/foo/revisions/2/diff/#{@path_hash[:path]}" },
|
||||
{ :controller => 'repositories', :action => 'diff', :id => 'redmine', :repository_id => 'foo',
|
||||
:path => @path_hash[:param], :rev => '2' }
|
||||
)
|
||||
assert_routing(
|
||||
{ :method => 'get',
|
||||
:path => "/projects/redmine/repository/foo/revisions/2/entry/#{@path_hash[:path]}" },
|
||||
{ :controller => 'repositories', :action => 'entry', :id => 'redmine', :repository_id => 'foo',
|
||||
:path => @path_hash[:param], :rev => '2' }
|
||||
)
|
||||
assert_routing(
|
||||
{ :method => 'get',
|
||||
:path => "/projects/redmine/repository/foo/revisions/2/raw/#{@path_hash[:path]}" },
|
||||
{ :controller => 'repositories', :action => 'entry', :id => 'redmine', :repository_id => 'foo',
|
||||
:path => @path_hash[:param], :rev => '2', :format => 'raw' }
|
||||
)
|
||||
assert_routing(
|
||||
{ :method => 'get',
|
||||
:path => "/projects/redmine/repository/foo/revisions/2/annotate/#{@path_hash[:path]}" },
|
||||
{ :controller => 'repositories', :action => 'annotate', :id => 'redmine', :repository_id => 'foo',
|
||||
:path => @path_hash[:param], :rev => '2' }
|
||||
)
|
||||
end
|
||||
|
||||
def test_repositories_non_revisions_path
|
||||
assert_routing(
|
||||
{ :method => 'get',
|
||||
|
@ -192,7 +296,46 @@ class RoutingRepositoriesTest < ActionController::IntegrationTest
|
|||
)
|
||||
end
|
||||
|
||||
private
|
||||
def test_repositories_non_revisions_path_with_repository_id
|
||||
assert_routing(
|
||||
{ :method => 'get',
|
||||
:path => "/projects/redmine/repository/foo/diff/#{@path_hash[:path]}" },
|
||||
{ :controller => 'repositories', :action => 'diff', :id => 'redmine', :repository_id => 'foo',
|
||||
:path => @path_hash[:param] }
|
||||
)
|
||||
assert_routing(
|
||||
{ :method => 'get',
|
||||
:path => "/projects/redmine/repository/foo/browse/#{@path_hash[:path]}" },
|
||||
{ :controller => 'repositories', :action => 'browse', :id => 'redmine', :repository_id => 'foo',
|
||||
:path => @path_hash[:param] }
|
||||
)
|
||||
assert_routing(
|
||||
{ :method => 'get',
|
||||
:path => "/projects/redmine/repository/foo/entry/#{@path_hash[:path]}" },
|
||||
{ :controller => 'repositories', :action => 'entry', :id => 'redmine', :repository_id => 'foo',
|
||||
:path => @path_hash[:param] }
|
||||
)
|
||||
assert_routing(
|
||||
{ :method => 'get',
|
||||
:path => "/projects/redmine/repository/foo/raw/#{@path_hash[:path]}" },
|
||||
{ :controller => 'repositories', :action => 'entry', :id => 'redmine', :repository_id => 'foo',
|
||||
:path => @path_hash[:param], :format => 'raw' }
|
||||
)
|
||||
assert_routing(
|
||||
{ :method => 'get',
|
||||
:path => "/projects/redmine/repository/foo/annotate/#{@path_hash[:path]}" },
|
||||
{ :controller => 'repositories', :action => 'annotate', :id => 'redmine', :repository_id => 'foo',
|
||||
:path => @path_hash[:param] }
|
||||
)
|
||||
assert_routing(
|
||||
{ :method => 'get',
|
||||
:path => "/projects/redmine/repository/foo/changes/#{@path_hash[:path]}" },
|
||||
{ :controller => 'repositories', :action => 'changes', :id => 'redmine', :repository_id => 'foo',
|
||||
:path => @path_hash[:param] }
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def repository_path_hash(arr)
|
||||
hs = {}
|
||||
|
|
|
@ -50,6 +50,19 @@ class RepositoryTest < ActiveSupport::TestCase
|
|||
assert_equal repository, project.repository
|
||||
end
|
||||
|
||||
def test_first_repository_should_be_set_as_default
|
||||
repository1 = Repository::Subversion.new(:project => Project.find(3), :identifier => 'svn1', :url => 'file:///svn1')
|
||||
assert repository1.save
|
||||
assert repository1.is_default?
|
||||
|
||||
repository2 = Repository::Subversion.new(:project => Project.find(3), :identifier => 'svn2', :url => 'file:///svn2')
|
||||
assert repository2.save
|
||||
assert !repository2.is_default?
|
||||
|
||||
assert_equal repository1, Project.find(3).repository
|
||||
assert_equal [repository1, repository2], Project.find(3).repositories.sort
|
||||
end
|
||||
|
||||
def test_destroy
|
||||
changesets = Changeset.count(:all, :conditions => "repository_id = 10")
|
||||
changes = Change.count(:all, :conditions => "repository_id = 10",
|
||||
|
|
|
@ -316,9 +316,8 @@ class UserTest < ActiveSupport::TestCase
|
|||
|
||||
def test_destroy_should_nullify_changesets
|
||||
changeset = Changeset.create!(
|
||||
:repository => Repository::Subversion.create!(
|
||||
:project_id => 1,
|
||||
:url => 'file:///var/svn'
|
||||
:repository => Repository::Subversion.generate!(
|
||||
:project_id => 1
|
||||
),
|
||||
:revision => '12',
|
||||
:committed_on => Time.now,
|
||||
|
|
Loading…
Reference in New Issue