diff options
32 files changed, 383 insertions, 77 deletions
diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index 0a95c6ade..539dcd9dc 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -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]) - @repository = @project.repository + 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 ||= '' diff --git a/app/controllers/sys_controller.rb b/app/controllers/sys_controller.rb index 89ad19ad4..0644fed84 100644 --- a/app/controllers/sys_controller.rb +++ b/app/controllers/sys_controller.rb @@ -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 diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 1954351e5..968420041 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -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 diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb index f3b229f2c..24807139e 100644 --- a/app/helpers/repositories_helper.rb +++ b/app/helpers/repositories_helper.rb @@ -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? diff --git a/app/models/project.rb b/app/models/project.rb index 29578df6e..3760a646c 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -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 diff --git a/app/models/repository.rb b/app/models/repository.rb index c231a8724..aad078c7d 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -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,7 +73,9 @@ class Repository < ActiveRecord::Base end send :attributes_without_extra_info=, p, guard_protected_attributes - merge_extra_info(p_extra) + if p_extra.keys.any? + merge_extra_info(p_extra) + end end # Removes leading and trailing whitespace @@ -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 diff --git a/app/views/issues/_changesets.html.erb b/app/views/issues/_changesets.html.erb index 76e9a2810..beab65536 100644 --- a/app/views/issues/_changesets.html.erb +++ b/app/views/issues/_changesets.html.erb @@ -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"> diff --git a/app/views/projects/settings/_repositories.html.erb b/app/views/projects/settings/_repositories.html.erb index d74220b9e..fc3fd923e 100644 --- a/app/views/projects/settings/_repositories.html.erb +++ b/app/views/projects/settings/_repositories.html.erb @@ -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 %> diff --git a/app/views/repositories/_breadcrumbs.html.erb b/app/views/repositories/_breadcrumbs.html.erb index 0d548176c..1a610ff6c 100644 --- a/app/views/repositories/_breadcrumbs.html.erb +++ b/app/views/repositories/_breadcrumbs.html.erb @@ -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 %> <% diff --git a/app/views/repositories/_dir_list_content.html.erb b/app/views/repositories/_dir_list_content.html.erb index 4a1779e28..057b7dc2e 100644 --- a/app/views/repositories/_dir_list_content.html.erb +++ b/app/views/repositories/_dir_list_content.html.erb @@ -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> diff --git a/app/views/repositories/_form.html.erb b/app/views/repositories/_form.html.erb index bd1f61334..b233a13df 100644 --- a/app/views/repositories/_form.html.erb +++ b/app/views/repositories/_form.html.erb @@ -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 %> diff --git a/app/views/repositories/_link_to_functions.html.erb b/app/views/repositories/_link_to_functions.html.erb index 412d5f86f..aed88ebb7 100644 --- a/app/views/repositories/_link_to_functions.html.erb +++ b/app/views/repositories/_link_to_functions.html.erb @@ -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> diff --git a/app/views/repositories/_navigation.html.erb b/app/views/repositories/_navigation.html.erb index 706f06c80..05c19d9be 100644 --- a/app/views/repositories/_navigation.html.erb +++ b/app/views/repositories/_navigation.html.erb @@ -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 -%> diff --git a/app/views/repositories/_revisions.html.erb b/app/views/repositories/_revisions.html.erb index ea55e4bed..3fd1cf690 100644 --- a/app/views/repositories/_revisions.html.erb +++ b/app/views/repositories/_revisions.html.erb @@ -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> diff --git a/app/views/repositories/annotate.html.erb b/app/views/repositories/annotate.html.erb index 444ae2a3a..188d9215d 100644 --- a/app/views/repositories/annotate.html.erb +++ b/app/views/repositories/annotate.html.erb @@ -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> diff --git a/app/views/repositories/revision.html.erb b/app/views/repositories/revision.html.erb index 6163ce17c..7c44cd674 100644 --- a/app/views/repositories/revision.html.erb +++ b/app/views/repositories/revision.html.erb @@ -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> diff --git a/app/views/repositories/revisions.html.erb b/app/views/repositories/revisions.html.erb index 8974fa788..f1d442f08 100644 --- a/app/views/repositories/revisions.html.erb +++ b/app/views/repositories/revisions.html.erb @@ -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 %> diff --git a/app/views/repositories/show.html.erb b/app/views/repositories/show.html.erb index fc148e8d1..ca64c73f3 100644 --- a/app/views/repositories/show.html.erb +++ b/app/views/repositories/show.html.erb @@ -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 %> diff --git a/app/views/repositories/stats.html.erb b/app/views/repositories/stats.html.erb index e5e737506..b9a5d7d6a 100644 --- a/app/views/repositories/stats.html.erb +++ b/app/views/repositories/stats.html.erb @@ -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> diff --git a/config/locales/en.yml b/config/locales/en.yml index 489011886..6dc4cb3d7 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -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 diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 7f04e5cb6..780f634d9 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -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 diff --git a/config/routes.rb b/config/routes.rb index 64ba34d2f..1a02d02c3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -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', + 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/show/*path', + 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' - repository_views.connect 'projects/:id/repository/graph', - :action => 'graph' end repositories.connect 'projects/:id/repository/revision', diff --git a/db/migrate/20120115143024_add_repositories_identifier.rb b/db/migrate/20120115143024_add_repositories_identifier.rb new file mode 100644 index 000000000..b54ebd1d5 --- /dev/null +++ b/db/migrate/20120115143024_add_repositories_identifier.rb @@ -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 diff --git a/db/migrate/20120115143100_add_repositories_is_default.rb b/db/migrate/20120115143100_add_repositories_is_default.rb new file mode 100644 index 000000000..87f018120 --- /dev/null +++ b/db/migrate/20120115143100_add_repositories_is_default.rb @@ -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 diff --git a/db/migrate/20120115143126_set_default_repositories.rb b/db/migrate/20120115143126_set_default_repositories.rb new file mode 100644 index 000000000..cd7674e71 --- /dev/null +++ b/db/migrate/20120115143126_set_default_repositories.rb @@ -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 diff --git a/public/stylesheets/application.css b/public/stylesheets/application.css index 61c522a5a..8410da60f 100644 --- a/public/stylesheets/application.css +++ b/public/stylesheets/application.css @@ -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; } diff --git a/test/exemplars/repository_exemplar.rb b/test/exemplars/repository_exemplar.rb index 7b596ea36..961bee7ae 100644 --- a/test/exemplars/repository_exemplar.rb +++ b/test/exemplars/repository_exemplar.rb @@ -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 diff --git a/test/fixtures/repositories.yml b/test/fixtures/repositories.yml index 61930f395..ef7285f61 100644 --- a/test/fixtures/repositories.yml +++ b/test/fixtures/repositories.yml @@ -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 diff --git a/test/functional/repositories_controller_test.rb b/test/functional/repositories_controller_test.rb index 4310c7630..817e16e2a 100644 --- a/test/functional/repositories_controller_test.rb +++ b/test/functional/repositories_controller_test.rb @@ -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 diff --git a/test/integration/routing/repositories_test.rb b/test/integration/routing/repositories_test.rb index edc49e70b..d2573eb36 100644 --- a/test/integration/routing/repositories_test.rb +++ b/test/integration/routing/repositories_test.rb @@ -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 = {} diff --git a/test/unit/repository_test.rb b/test/unit/repository_test.rb index ad0fc9f5f..275dc8bee 100644 --- a/test/unit/repository_test.rb +++ b/test/unit/repository_test.rb @@ -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", diff --git a/test/unit/user_test.rb b/test/unit/user_test.rb index 4a1cb3337..a9484501d 100644 --- a/test/unit/user_test.rb +++ b/test/unit/user_test.rb @@ -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, |