##// END OF EJS Templates
Added branch and tag support to the git repository viewer. (#1406)...
Eric Davis -
r2735:c28b044d6802
parent child
Show More
@@ -0,0 +1,21
1 <%= link_to 'root', :action => 'show', :id => @project, :path => '', :rev => @rev %>
2 <%
3 dirs = path.split('/')
4 if 'file' == kind
5 filename = dirs.pop
6 end
7 link_path = ''
8 dirs.each do |dir|
9 next if dir.blank?
10 link_path << '/' unless link_path.empty?
11 link_path << "#{dir}"
12 %>
13 / <%= link_to h(dir), :action => 'show', :id => @project, :path => to_path_param(link_path), :rev => @rev %>
14 <% end %>
15 <% if filename %>
16 / <%= link_to h(filename), :action => 'changes', :id => @project, :path => to_path_param("#{link_path}/#{filename}"), :rev => @rev %>
17 <% end %>
18
19 <%= "@ #{revision}" if revision %>
20
21 <% html_title(with_leading_slash(path)) -%>
@@ -0,0 +1,35
1 Event.observe(window,'load',function() {
2 /*
3 If we're viewing a tag or branch, don't display it in the
4 revision box
5 */
6 var branch_selected = $('branch') && $('rev').getValue() == $('branch').getValue();
7 var tag_selected = $('tag') && $('rev').getValue() == $('tag').getValue();
8 if (branch_selected || tag_selected) {
9 $('rev').setValue('');
10 }
11
12 /*
13 Copy the branch/tag value into the revision box, then disable
14 the dropdowns before submitting the form
15 */
16 $$('#branch,#tag').each(function(e) {
17 e.observe('change',function(e) {
18 $('rev').setValue(e.element().getValue());
19 $$('#branch,#tag').invoke('disable');
20 e.element().parentNode.submit();
21 $$('#branch,#tag').invoke('enable');
22 });
23 });
24
25 /*
26 Disable the branch/tag dropdowns before submitting the revision form
27 */
28 $('rev').observe('keydown', function(e) {
29 if (e.keyCode == 13) {
30 $$('#branch,#tag').invoke('disable');
31 e.element().parentNode.submit();
32 $$('#branch,#tag').invoke('enable');
33 }
34 });
35 })
@@ -0,0 +1,22
1 require File.dirname(__FILE__) + '/../test_helper'
2
3 class GitAdapterTest < Test::Unit::TestCase
4 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/git_repository'
5
6 if File.directory?(REPOSITORY_PATH)
7 def setup
8 @adapter = Redmine::Scm::Adapters::GitAdapter.new(REPOSITORY_PATH)
9 end
10
11 def test_branches
12 assert_equal @adapter.branches, ['master', 'test_branch']
13 end
14
15 def test_getting_all_revisions
16 assert_equal 12, @adapter.revisions('',nil,nil,:all => true).length
17 end
18 else
19 puts "Git test repository NOT FOUND. Skipping unit tests !!!"
20 def test_fake; assert true end
21 end
22 end
@@ -64,31 +64,26 class RepositoriesController < ApplicationController
64 redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'repository'
64 redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'repository'
65 end
65 end
66
66
67 def show
67 def show
68 # check if new revisions have been committed in the repository
68 @repository.fetch_changesets if Setting.autofetch_changesets? && @path.empty?
69 @repository.fetch_changesets if Setting.autofetch_changesets?
69
70 # root entries
71 @entries = @repository.entries('', @rev)
72 # latest changesets
73 @changesets = @repository.changesets.find(:all, :limit => 10, :order => "committed_on DESC")
74 show_error_not_found unless @entries || @changesets.any?
75 end
76
77 def browse
78 @entries = @repository.entries(@path, @rev)
70 @entries = @repository.entries(@path, @rev)
79 if request.xhr?
71 if request.xhr?
80 @entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
72 @entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
81 else
73 else
82 show_error_not_found and return unless @entries
74 show_error_not_found and return unless @entries
75 @changesets = @repository.latest_changesets(@path, @rev)
83 @properties = @repository.properties(@path, @rev)
76 @properties = @repository.properties(@path, @rev)
84 render :action => 'browse'
77 render :action => 'show'
85 end
78 end
86 end
79 end
80
81 alias_method :browse, :show
87
82
88 def changes
83 def changes
89 @entry = @repository.entry(@path, @rev)
84 @entry = @repository.entry(@path, @rev)
90 show_error_not_found and return unless @entry
85 show_error_not_found and return unless @entry
91 @changesets = @repository.changesets_for_path(@path, :limit => Setting.repository_log_display_limit.to_i)
86 @changesets = @repository.latest_changesets(@path, @rev, Setting.repository_log_display_limit.to_i)
92 @properties = @repository.properties(@path, @rev)
87 @properties = @repository.properties(@path, @rev)
93 end
88 end
94
89
@@ -135,7 +130,7 class RepositoriesController < ApplicationController
135 end
130 end
136
131
137 def revision
132 def revision
138 @changeset = @repository.changesets.find_by_revision(@rev)
133 @changeset = @repository.changesets.find(:first, :conditions => ["revision LIKE ?", @rev + '%'])
139 raise ChangesetNotFound unless @changeset
134 raise ChangesetNotFound unless @changeset
140
135
141 respond_to do |format|
136 respond_to do |format|
@@ -199,17 +194,14 private
199 render_404
194 render_404
200 end
195 end
201
196
202 REV_PARAM_RE = %r{^[a-f0-9]*$}
203
204 def find_repository
197 def find_repository
205 @project = Project.find(params[:id])
198 @project = Project.find(params[:id])
206 @repository = @project.repository
199 @repository = @project.repository
207 render_404 and return false unless @repository
200 render_404 and return false unless @repository
208 @path = params[:path].join('/') unless params[:path].nil?
201 @path = params[:path].join('/') unless params[:path].nil?
209 @path ||= ''
202 @path ||= ''
210 @rev = params[:rev]
203 @rev = params[:rev].blank? ? @repository.default_branch : params[:rev].strip
211 @rev_to = params[:rev_to]
204 @rev_to = params[:rev_to]
212 raise InvalidRevisionParam unless @rev.to_s.match(REV_PARAM_RE) && @rev.to_s.match(REV_PARAM_RE)
213 rescue ActiveRecord::RecordNotFound
205 rescue ActiveRecord::RecordNotFound
214 render_404
206 render_404
215 rescue InvalidRevisionParam
207 rescue InvalidRevisionParam
@@ -62,6 +62,18 class Repository < ActiveRecord::Base
62 def entries(path=nil, identifier=nil)
62 def entries(path=nil, identifier=nil)
63 scm.entries(path, identifier)
63 scm.entries(path, identifier)
64 end
64 end
65
66 def branches
67 scm.branches
68 end
69
70 def tags
71 scm.tags
72 end
73
74 def default_branch
75 scm.default_branch
76 end
65
77
66 def properties(path, identifier=nil)
78 def properties(path, identifier=nil)
67 scm.properties(path, identifier)
79 scm.properties(path, identifier)
@@ -92,11 +104,15 class Repository < ActiveRecord::Base
92 def latest_changeset
104 def latest_changeset
93 @latest_changeset ||= changesets.find(:first)
105 @latest_changeset ||= changesets.find(:first)
94 end
106 end
107
108 def latest_changesets(path,rev,limit=10)
109 @latest_changesets ||= changesets.find(:all, limit, :order => "committed_on DESC")
110 end
95
111
96 def scan_changesets_for_issue_ids
112 def scan_changesets_for_issue_ids
97 self.changesets.each(&:scan_comment_for_issue_ids)
113 self.changesets.each(&:scan_comment_for_issue_ids)
98 end
114 end
99
115
100 # Returns an array of committers usernames and associated user_id
116 # Returns an array of committers usernames and associated user_id
101 def committers
117 def committers
102 @committers ||= Changeset.connection.select_rows("SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}")
118 @committers ||= Changeset.connection.select_rows("SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}")
@@ -29,43 +29,60 class Repository::Git < Repository
29 'Git'
29 'Git'
30 end
30 end
31
31
32 def branches
33 scm.branches
34 end
35
36 def tags
37 scm.tags
38 end
39
32 def changesets_for_path(path, options={})
40 def changesets_for_path(path, options={})
33 Change.find(:all, :include => {:changeset => :user},
41 Change.find(
34 :conditions => ["repository_id = ? AND path = ?", id, path],
42 :all,
35 :order => "committed_on DESC, #{Changeset.table_name}.revision DESC",
43 :include => {:changeset => :user},
36 :limit => options[:limit]).collect(&:changeset)
44 :conditions => ["repository_id = ? AND path = ?", id, path],
45 :order => "committed_on DESC, #{Changeset.table_name}.revision DESC",
46 :limit => options[:limit]
47 ).collect(&:changeset)
37 end
48 end
38
49
50 # With SCM's that have a sequential commit numbering, redmine is able to be
51 # clever and only fetch changesets going forward from the most recent one
52 # it knows about. However, with git, you never know if people have merged
53 # commits into the middle of the repository history, so we always have to
54 # parse the entire log.
39 def fetch_changesets
55 def fetch_changesets
40 scm_info = scm.info
56 # Save ourselves an expensive operation if we're already up to date
41 if scm_info
57 return if scm.num_revisions == changesets.count
42 # latest revision found in database
58
43 db_revision = latest_changeset ? latest_changeset.revision : nil
59 revisions = scm.revisions('', nil, nil, :all => true)
44 # latest revision in the repository
60 return if revisions.nil? || revisions.empty?
45 scm_revision = scm_info.lastrev.scmid
61
62 # Find revisions that redmine knows about already
63 existing_revisions = changesets.find(:all).map!{|c| c.scmid}
64
65 # Clean out revisions that are no longer in git
66 Changeset.delete_all(["scmid NOT IN (?) AND repository_id = (?)", revisions.map{|r| r.scmid}, self.id])
67
68 # Subtract revisions that redmine already knows about
69 revisions.reject!{|r| existing_revisions.include?(r.scmid)}
70
71 # Save the remaining ones to the database
72 revisions.each{|r| r.save(self)} unless revisions.nil?
73 end
74
75 def latest_changesets(path,rev,limit=10)
76 revisions = scm.revisions(path, nil, rev, :limit => limit, :all => false)
77 return [] if revisions.nil? || revisions.empty?
46
78
47 unless changesets.find_by_scmid(scm_revision)
79 changesets.find(
48 scm.revisions('', db_revision, nil, :reverse => true) do |revision|
80 :all,
49 if changesets.find_by_scmid(revision.scmid.to_s).nil?
81 :conditions => [
50 transaction do
82 "scmid IN (?)",
51 changeset = Changeset.create!(:repository => self,
83 revisions.map!{|c| c.scmid}
52 :revision => revision.identifier,
84 ],
53 :scmid => revision.scmid,
85 :order => 'committed_on DESC'
54 :committer => revision.author,
86 )
55 :committed_on => revision.time,
56 :comments => revision.message)
57
58 revision.paths.each do |change|
59 Change.create!(:changeset => changeset,
60 :action => change[:action],
61 :path => change[:path],
62 :from_path => change[:from_path],
63 :from_revision => change[:from_revision])
64 end
65 end
66 end
67 end
68 end
69 end
70 end
87 end
71 end
88 end
@@ -4,7 +4,7
4 <tr id="<%= tr_id %>" class="<%= params[:parent_id] %> entry <%= entry.kind %>">
4 <tr id="<%= tr_id %>" class="<%= params[:parent_id] %> entry <%= entry.kind %>">
5 <td style="padding-left: <%=18 * depth%>px;" class="filename">
5 <td style="padding-left: <%=18 * depth%>px;" class="filename">
6 <% if entry.is_dir? %>
6 <% if entry.is_dir? %>
7 <span class="expander" onclick="<%= remote_function :url => {:action => 'browse', :id => @project, :path => to_path_param(entry.path), :rev => @rev, :depth => (depth + 1), :parent_id => tr_id},
7 <span class="expander" onclick="<%= remote_function :url => {:action => 'show', :id => @project, :path => to_path_param(entry.path), :rev => @rev, :depth => (depth + 1), :parent_id => tr_id},
8 :method => :get,
8 :method => :get,
9 :update => { :success => tr_id },
9 :update => { :success => tr_id },
10 :position => :after,
10 :position => :after,
@@ -12,7 +12,7
12 :condition => "scmEntryClick('#{tr_id}')"%>">&nbsp</span>
12 :condition => "scmEntryClick('#{tr_id}')"%>">&nbsp</span>
13 <% end %>
13 <% end %>
14 <%= link_to h(entry.name),
14 <%= link_to h(entry.name),
15 {:action => (entry.is_dir? ? 'browse' : 'changes'), :id => @project, :path => to_path_param(entry.path), :rev => @rev},
15 {:action => (entry.is_dir? ? 'show' : 'changes'), :id => @project, :path => to_path_param(entry.path), :rev => @rev},
16 :class => (entry.is_dir? ? 'icon icon-folder' : "icon icon-file #{Redmine::MimeType.css_class_of(entry.name)}")%>
16 :class => (entry.is_dir? ? 'icon icon-folder' : "icon icon-file #{Redmine::MimeType.css_class_of(entry.name)}")%>
17 </td>
17 </td>
18 <td class="size"><%= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %></td>
18 <td class="size"><%= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %></td>
@@ -1,21 +1,21
1 <%= link_to 'root', :action => 'browse', :id => @project, :path => '', :rev => @rev %>
1 <% content_for :header_tags do %>
2 <%
2 <%= javascript_include_tag 'repository_navigation' %>
3 dirs = path.split('/')
4 if 'file' == kind
5 filename = dirs.pop
6 end
7 link_path = ''
8 dirs.each do |dir|
9 next if dir.blank?
10 link_path << '/' unless link_path.empty?
11 link_path << "#{dir}"
12 %>
13 / <%= link_to h(dir), :action => 'browse', :id => @project, :path => to_path_param(link_path), :rev => @rev %>
14 <% end %>
15 <% if filename %>
16 / <%= link_to h(filename), :action => 'changes', :id => @project, :path => to_path_param("#{link_path}/#{filename}"), :rev => @rev %>
17 <% end %>
3 <% end %>
18
4
19 <%= "@ #{revision}" if revision %>
5 <%= link_to l(:label_statistics), {:action => 'stats', :id => @project}, :class => 'icon icon-stats' %>
6
7 <% form_tag({:action => controller.action_name, :id => @project, :path => @path, :rev => ''}, {:method => :get, :id => 'revision_selector'}) do -%>
8 <!-- Branches Dropdown -->
9 <% if !@repository.branches.nil? && @repository.branches.length > 0 -%>
10 | <%= l(:label_branch) %>:
11 <%= select_tag :branch, options_for_select([''] + @repository.branches,@rev), :id => 'branch' %>
12 <% end -%>
13
14 <% if !@repository.tags.nil? && @repository.tags.length > 0 -%>
15 | <%= l(:label_tag) %>:
16 <%= select_tag :tag, options_for_select([''] + @repository.tags,@rev), :id => 'tag' %>
17 <% end -%>
20
18
21 <% html_title(with_leading_slash(path)) -%>
19 | <%= l(:label_revision) %>:
20 <%= text_field_tag 'rev', @rev, :size => 8 %>
21 <% end -%>
@@ -1,4 +1,10
1 <h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => 'file', :revision => @rev } %></h2>
1 <%= call_hook(:view_repositories_show_contextual, { :repository => @repository, :project => @project }) %>
2
3 <div class="contextual">
4 <%= render :partial => 'navigation' %>
5 </div>
6
7 <h2><%= render :partial => 'breadcrumbs', :locals => { :path => @path, :kind => 'file', :revision => @rev } %></h2>
2
8
3 <p><%= render :partial => 'link_to_functions' %></p>
9 <p><%= render :partial => 'link_to_functions' %></p>
4
10
@@ -1,10 +1,8
1 <div class="contextual">
1 <div class="contextual">
2 <% form_tag({}, :method => :get) do %>
2 <%= render :partial => 'navigation' %>
3 <%= l(:label_revision) %>: <%= text_field_tag 'rev', @rev, :size => 5 %>
4 <% end %>
5 </div>
3 </div>
6
4
7 <h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => 'dir', :revision => @rev } %></h2>
5 <h2><%= render :partial => 'breadcrumbs', :locals => { :path => @path, :kind => 'dir', :revision => @rev } %></h2>
8
6
9 <%= render :partial => 'dir_list' %>
7 <%= render :partial => 'dir_list' %>
10 <%= render_properties(@properties) %>
8 <%= render_properties(@properties) %>
@@ -1,4 +1,12
1 <h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => (@entry ? @entry.kind : nil), :revision => @rev } %></h2>
1 <%= call_hook(:view_repositories_show_contextual, { :repository => @repository, :project => @project }) %>
2
3 <div class="contextual">
4 <%= render :partial => 'navigation' %>
5 </div>
6
7 <h2>
8 <%= render :partial => 'breadcrumbs', :locals => { :path => @path, :kind => (@entry ? @entry.kind : nil), :revision => @rev } %>
9 </h2>
2
10
3 <p><%= render :partial => 'link_to_functions' %></p>
11 <p><%= render :partial => 'link_to_functions' %></p>
4
12
@@ -1,4 +1,10
1 <h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => 'file', :revision => @rev } %></h2>
1 <%= call_hook(:view_repositories_show_contextual, { :repository => @repository, :project => @project }) %>
2
3 <div class="contextual">
4 <%= render :partial => 'navigation' %>
5 </div>
6
7 <h2><%= render :partial => 'breadcrumbs', :locals => { :path => @path, :kind => 'file', :revision => @rev } %></h2>
2
8
3 <p><%= render :partial => 'link_to_functions' %></p>
9 <p><%= render :partial => 'link_to_functions' %></p>
4
10
@@ -14,7 +14,7
14 &#187;&nbsp;
14 &#187;&nbsp;
15
15
16 <% form_tag({:controller => 'repositories', :action => 'revision', :id => @project, :rev => nil}, :method => :get) do %>
16 <% form_tag({:controller => 'repositories', :action => 'revision', :id => @project, :rev => nil}, :method => :get) do %>
17 <%= text_field_tag 'rev', @rev, :size => 5 %>
17 <%= text_field_tag 'rev', @rev[0,8], :size => 8 %>
18 <%= submit_tag 'OK', :name => nil %>
18 <%= submit_tag 'OK', :name => nil %>
19 <% end %>
19 <% end %>
20 </div>
20 </div>
@@ -1,6 +1,6
1 <div class="contextual">
1 <div class="contextual">
2 <% form_tag({:action => 'revision', :id => @project}) do %>
2 <% form_tag({:action => 'revision', :id => @project}) do %>
3 <%= l(:label_revision) %>: <%= text_field_tag 'rev', @rev, :size => 5 %>
3 <%= l(:label_revision) %>: <%= text_field_tag 'rev', @rev, :size => 8 %>
4 <%= submit_tag 'OK' %>
4 <%= submit_tag 'OK' %>
5 <% end %>
5 <% end %>
6 </div>
6 </div>
@@ -1,15 +1,10
1 <div class="contextual">
2 <%= call_hook(:view_repositories_show_contextual, { :repository => @repository, :project => @project }) %>
1 <%= call_hook(:view_repositories_show_contextual, { :repository => @repository, :project => @project }) %>
3 <%= link_to l(:label_statistics), {:action => 'stats', :id => @project}, :class => 'icon icon-stats' %>
4
2
5 <% if !@entries.nil? && authorize_for('repositories', 'browse') -%>
3 <div class="contextual">
6 <% form_tag({:action => 'browse', :id => @project}, :method => :get) do -%>
4 <%= render :partial => 'navigation' %>
7 | <%= l(:label_revision) %>: <%= text_field_tag 'rev', @rev, :size => 5 %>
8 <% end -%>
9 <% end -%>
10 </div>
5 </div>
11
6
12 <h2><%= l(:label_repository) %> (<%= @repository.scm_name %>)</h2>
7 <h2><%= render :partial => 'breadcrumbs', :locals => { :path => @path, :kind => 'dir', :revision => @rev } %></h2>
13
8
14 <% if !@entries.nil? && authorize_for('repositories', 'browse') %>
9 <% if !@entries.nil? && authorize_for('repositories', 'browse') %>
15 <%= render :partial => 'dir_list' %>
10 <%= render :partial => 'dir_list' %>
@@ -18,7 +13,7
18 <% if !@changesets.empty? && authorize_for('repositories', 'revisions') %>
13 <% if !@changesets.empty? && authorize_for('repositories', 'revisions') %>
19 <h3><%= l(:label_latest_revision_plural) %></h3>
14 <h3><%= l(:label_latest_revision_plural) %></h3>
20 <%= render :partial => 'revisions', :locals => {:project => @project, :path => '', :revisions => @changesets, :entry => nil }%>
15 <%= render :partial => 'revisions', :locals => {:project => @project, :path => '', :revisions => @changesets, :entry => nil }%>
21 <p><%= link_to l(:label_view_revisions), :action => 'revisions', :id => @project %></p>
16 <p><%= link_to l(:label_view_all_revisions), :action => 'revisions', :id => @project %></p>
22 <% content_for :header_tags do %>
17 <% content_for :header_tags do %>
23 <%= auto_discovery_link_tag(:atom, params.merge({:format => 'atom', :action => 'revisions', :id => @project, :page => nil, :key => User.current.rss_key})) %>
18 <%= auto_discovery_link_tag(:atom, params.merge({:format => 'atom', :action => 'revisions', :id => @project, :page => nil, :key => User.current.rss_key})) %>
24 <% end %>
19 <% end %>
@@ -798,3 +798,6 bg:
798 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
798 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
799 permission_add_project: Create project
799 permission_add_project: Create project
800 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
800 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
801 label_view_all_revisions: View all revisions
802 label_tag: Tag
803 label_branch: Branch
@@ -831,3 +831,6 bs:
831 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
831 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
832 permission_add_project: Create project
832 permission_add_project: Create project
833 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
833 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
834 label_view_all_revisions: View all revisions
835 label_tag: Tag
836 label_branch: Branch
@@ -801,3 +801,6 ca:
801 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
801 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
802 permission_add_project: Create project
802 permission_add_project: Create project
803 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
803 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
804 label_view_all_revisions: View all revisions
805 label_tag: Tag
806 label_branch: Branch
@@ -804,3 +804,6 cs:
804 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
804 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
805 permission_add_project: Create project
805 permission_add_project: Create project
806 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
806 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
807 label_view_all_revisions: View all revisions
808 label_tag: Tag
809 label_branch: Branch
@@ -831,3 +831,6 da:
831 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
831 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
832 permission_add_project: Create project
832 permission_add_project: Create project
833 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
833 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
834 label_view_all_revisions: View all revisions
835 label_tag: Tag
836 label_branch: Branch
@@ -830,3 +830,6 de:
830 mail_body_wiki_content_updated: "Die Wiki-Seite '{{page}}' wurde von {{author}} aktualisiert."
830 mail_body_wiki_content_updated: "Die Wiki-Seite '{{page}}' wurde von {{author}} aktualisiert."
831 permission_add_project: Erstelle Projekt
831 permission_add_project: Erstelle Projekt
832 setting_new_project_user_role_id: Rolle einem Nicht-Administrator zugeordnet, welcher ein Projekt erstellt
832 setting_new_project_user_role_id: Rolle einem Nicht-Administrator zugeordnet, welcher ein Projekt erstellt
833 label_view_all_revisions: View all revisions
834 label_tag: Tag
835 label_branch: Branch
@@ -543,6 +543,8 en:
543 label_browse: Browse
543 label_browse: Browse
544 label_modification: "{{count}} change"
544 label_modification: "{{count}} change"
545 label_modification_plural: "{{count}} changes"
545 label_modification_plural: "{{count}} changes"
546 label_branch: Branch
547 label_tag: Tag
546 label_revision: Revision
548 label_revision: Revision
547 label_revision_plural: Revisions
549 label_revision_plural: Revisions
548 label_associated_revisions: Associated revisions
550 label_associated_revisions: Associated revisions
@@ -554,6 +556,7 en:
554 label_latest_revision: Latest revision
556 label_latest_revision: Latest revision
555 label_latest_revision_plural: Latest revisions
557 label_latest_revision_plural: Latest revisions
556 label_view_revisions: View revisions
558 label_view_revisions: View revisions
559 label_view_all_revisions: View all revisions
557 label_max_size: Maximum size
560 label_max_size: Maximum size
558 label_sort_highest: Move to top
561 label_sort_highest: Move to top
559 label_sort_higher: Move up
562 label_sort_higher: Move up
@@ -851,3 +851,6 es:
851 mail_body_wiki_content_updated: La página wiki '{{page}}' ha sido actualizada por {{author}}.
851 mail_body_wiki_content_updated: La página wiki '{{page}}' ha sido actualizada por {{author}}.
852 permission_add_project: Crear proyecto
852 permission_add_project: Crear proyecto
853 setting_new_project_user_role_id: Permiso asignado a un usuario no-administrador para crear proyectos
853 setting_new_project_user_role_id: Permiso asignado a un usuario no-administrador para crear proyectos
854 label_view_all_revisions: View all revisions
855 label_tag: Tag
856 label_branch: Branch
@@ -841,3 +841,6 fi:
841 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
841 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
842 permission_add_project: Create project
842 permission_add_project: Create project
843 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
843 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
844 label_view_all_revisions: View all revisions
845 label_tag: Tag
846 label_branch: Branch
@@ -832,4 +832,7 fr:
832 enumeration_doc_categories: Catégories des documents
832 enumeration_doc_categories: Catégories des documents
833 enumeration_activities: Activités (suivi du temps)
833 enumeration_activities: Activités (suivi du temps)
834 label_greater_or_equal: ">="
834 label_greater_or_equal: ">="
835 label_less_or_equal: <=
835 label_less_or_equal: "<="
836 label_view_all_revisions: View all revisions
837 label_tag: Tag
838 label_branch: Branch
@@ -830,3 +830,6 gl:
830 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
830 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
831 permission_add_project: Create project
831 permission_add_project: Create project
832 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
832 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
833 label_view_all_revisions: View all revisions
834 label_tag: Tag
835 label_branch: Branch
@@ -813,3 +813,6 he:
813 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
813 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
814 permission_add_project: Create project
814 permission_add_project: Create project
815 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
815 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
816 label_view_all_revisions: View all revisions
817 label_tag: Tag
818 label_branch: Branch
@@ -836,3 +836,6
836 mail_body_wiki_content_updated: A '{{page}}' wiki oldalt {{author}} frissítette.
836 mail_body_wiki_content_updated: A '{{page}}' wiki oldalt {{author}} frissítette.
837 permission_add_project: Projekt létrehozása
837 permission_add_project: Projekt létrehozása
838 setting_new_project_user_role_id: Projekt létrehozási jog nem adminisztrátor felhasználóknak
838 setting_new_project_user_role_id: Projekt létrehozási jog nem adminisztrátor felhasználóknak
839 label_view_all_revisions: View all revisions
840 label_tag: Tag
841 label_branch: Branch
@@ -816,3 +816,6 it:
816 mail_body_wiki_content_updated: La pagina '{{page}}' wiki è stata aggiornata da{{author}}.
816 mail_body_wiki_content_updated: La pagina '{{page}}' wiki è stata aggiornata da{{author}}.
817 permission_add_project: Crea progetto
817 permission_add_project: Crea progetto
818 setting_new_project_user_role_id: Ruolo assegnato agli utenti non amministratori che creano un progetto
818 setting_new_project_user_role_id: Ruolo assegnato agli utenti non amministratori che creano un progetto
819 label_view_all_revisions: View all revisions
820 label_tag: Tag
821 label_branch: Branch
@@ -838,3 +838,6 ja:
838 enumeration_issue_priorities: チケットの優先度
838 enumeration_issue_priorities: チケットの優先度
839 enumeration_doc_categories: 文書カテゴリ
839 enumeration_doc_categories: 文書カテゴリ
840 enumeration_activities: 作業分類 (時間トラッキング)
840 enumeration_activities: 作業分類 (時間トラッキング)
841 label_view_all_revisions: View all revisions
842 label_tag: Tag
843 label_branch: Branch
@@ -869,3 +869,6 ko:
869 # by Kihyun Yoon(ddumbugie@gmail.com)
869 # by Kihyun Yoon(ddumbugie@gmail.com)
870 # by John Hwang (jhwang@tavon.org),http://github.com/tavon
870 # by John Hwang (jhwang@tavon.org),http://github.com/tavon
871 field_issue_to: Related issue
871 field_issue_to: Related issue
872 label_view_all_revisions: View all revisions
873 label_tag: Tag
874 label_branch: Branch
@@ -841,3 +841,6 lt:
841 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
841 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
842 permission_add_project: Create project
842 permission_add_project: Create project
843 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
843 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
844 label_view_all_revisions: View all revisions
845 label_tag: Tag
846 label_branch: Branch
@@ -786,3 +786,6 nl:
786 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
786 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
787 permission_add_project: Create project
787 permission_add_project: Create project
788 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
788 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
789 label_view_all_revisions: View all revisions
790 label_tag: Tag
791 label_branch: Branch
@@ -803,3 +803,6
803 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
803 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
804 permission_add_project: Create project
804 permission_add_project: Create project
805 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
805 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
806 label_view_all_revisions: View all revisions
807 label_tag: Tag
808 label_branch: Branch
@@ -834,3 +834,6 pl:
834 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
834 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
835 permission_add_project: Create project
835 permission_add_project: Create project
836 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
836 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
837 label_view_all_revisions: View all revisions
838 label_tag: Tag
839 label_branch: Branch
@@ -836,3 +836,6 pt-BR:
836 mail_body_wiki_content_updated: A página wiki '{{page}}' foi atualizada por {{author}}.
836 mail_body_wiki_content_updated: A página wiki '{{page}}' foi atualizada por {{author}}.
837 permission_add_project: Criar projeto
837 permission_add_project: Criar projeto
838 setting_new_project_user_role_id: Papel dado a um usuário não administrador que crie um projeto
838 setting_new_project_user_role_id: Papel dado a um usuário não administrador que crie um projeto
839 label_view_all_revisions: View all revisions
840 label_tag: Tag
841 label_branch: Branch
@@ -822,3 +822,6 pt:
822 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
822 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
823 permission_add_project: Create project
823 permission_add_project: Create project
824 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
824 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
825 label_view_all_revisions: View all revisions
826 label_tag: Tag
827 label_branch: Branch
@@ -801,3 +801,6 ro:
801 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
801 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
802 permission_add_project: Create project
802 permission_add_project: Create project
803 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
803 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
804 label_view_all_revisions: View all revisions
805 label_tag: Tag
806 label_branch: Branch
@@ -928,3 +928,6 ru:
928 mail_body_wiki_content_updated: "{{author}} обновил(а) wiki-страницу '{{page}}'."
928 mail_body_wiki_content_updated: "{{author}} обновил(а) wiki-страницу '{{page}}'."
929 permission_add_project: Создание проекта
929 permission_add_project: Создание проекта
930 setting_new_project_user_role_id: Роль, назначаемая пользователю, создавшему проект
930 setting_new_project_user_role_id: Роль, назначаемая пользователю, создавшему проект
931 label_view_all_revisions: View all revisions
932 label_tag: Tag
933 label_branch: Branch
@@ -802,4 +802,7 sk:
802 label_wiki_content_updated: Wiki stránka aktualizovaná
802 label_wiki_content_updated: Wiki stránka aktualizovaná
803 mail_body_wiki_content_updated: Wiki stránka '{{page}}' bola aktualizovaná užívateľom {{author}}.
803 mail_body_wiki_content_updated: Wiki stránka '{{page}}' bola aktualizovaná užívateľom {{author}}.
804 setting_repositories_encodings: Kódovanie repozitára
804 setting_repositories_encodings: Kódovanie repozitára
805 setting_new_project_user_role_id: Rola dána non-admin užívateľovi, ktorý vytvorí projekt No newline at end of file
805 setting_new_project_user_role_id: Rola dána non-admin užívateľovi, ktorý vytvorí projekt
806 label_view_all_revisions: View all revisions
807 label_tag: Tag
808 label_branch: Branch
@@ -800,3 +800,6 sl:
800 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
800 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
801 permission_add_project: Create project
801 permission_add_project: Create project
802 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
802 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
803 label_view_all_revisions: View all revisions
804 label_tag: Tag
805 label_branch: Branch
@@ -824,3 +824,6
824 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
824 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
825 permission_add_project: Create project
825 permission_add_project: Create project
826 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
826 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
827 label_view_all_revisions: View all revisions
828 label_tag: Tag
829 label_branch: Branch
@@ -858,3 +858,6 sv:
858 enumeration_issue_priorities: Ärendeprioriteter
858 enumeration_issue_priorities: Ärendeprioriteter
859 enumeration_doc_categories: Dokumentkategorier
859 enumeration_doc_categories: Dokumentkategorier
860 enumeration_activities: Aktiviteter (tidsuppföljning)
860 enumeration_activities: Aktiviteter (tidsuppföljning)
861 label_view_all_revisions: View all revisions
862 label_tag: Tag
863 label_branch: Branch
@@ -801,3 +801,6 th:
801 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
801 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
802 permission_add_project: Create project
802 permission_add_project: Create project
803 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
803 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
804 label_view_all_revisions: View all revisions
805 label_tag: Tag
806 label_branch: Branch
@@ -837,3 +837,6 tr:
837 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
837 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
838 permission_add_project: Create project
838 permission_add_project: Create project
839 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
839 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
840 label_view_all_revisions: View all revisions
841 label_tag: Tag
842 label_branch: Branch
@@ -800,3 +800,6 uk:
800 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
800 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
801 permission_add_project: Create project
801 permission_add_project: Create project
802 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
802 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
803 label_view_all_revisions: View all revisions
804 label_tag: Tag
805 label_branch: Branch
@@ -870,3 +870,6 vi:
870 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
870 mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
871 permission_add_project: Create project
871 permission_add_project: Create project
872 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
872 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
873 label_view_all_revisions: View all revisions
874 label_tag: Tag
875 label_branch: Branch
@@ -908,3 +908,6
908 enumeration_issue_priorities: 項目優先權
908 enumeration_issue_priorities: 項目優先權
909 enumeration_doc_categories: 文件分類
909 enumeration_doc_categories: 文件分類
910 enumeration_activities: 活動 (時間追蹤)
910 enumeration_activities: 活動 (時間追蹤)
911 label_view_all_revisions: View all revisions
912 label_tag: Tag
913 label_branch: Branch
@@ -833,3 +833,6 zh:
833 enumeration_issue_priorities: 问题优先级
833 enumeration_issue_priorities: 问题优先级
834 enumeration_doc_categories: 文档类别
834 enumeration_doc_categories: 文档类别
835 enumeration_activities: 活动(时间跟踪)
835 enumeration_activities: 活动(时间跟踪)
836 label_view_all_revisions: View all revisions
837 label_tag: Tag
838 label_branch: Branch
@@ -218,7 +218,7 ActionController::Routing::Routes.draw do |map|
218 repository_views.connect 'projects/:id/repository/revisions/:rev', :action => 'revision'
218 repository_views.connect 'projects/:id/repository/revisions/:rev', :action => 'revision'
219 repository_views.connect 'projects/:id/repository/revisions/:rev/diff', :action => 'diff'
219 repository_views.connect 'projects/:id/repository/revisions/:rev/diff', :action => 'diff'
220 repository_views.connect 'projects/:id/repository/revisions/:rev/diff.:format', :action => 'diff'
220 repository_views.connect 'projects/:id/repository/revisions/:rev/diff.:format', :action => 'diff'
221 repository_views.connect 'projects/:id/repository/revisions/:rev/:action/*path'
221 repository_views.connect 'projects/:id/repository/revisions/:rev/:action/*path', :requirements => { :rev => /[a-z0-9\.\-_]+/ }
222 repository_views.connect 'projects/:id/repository/:action/*path'
222 repository_views.connect 'projects/:id/repository/:action/*path'
223 end
223 end
224
224
@@ -1,153 +1,155
1 class Diff
1 module RedmineDiff
2 class Diff
2
3
3 VERSION = 0.3
4 VERSION = 0.3
4
5
5 def Diff.lcs(a, b)
6 def Diff.lcs(a, b)
6 astart = 0
7 astart = 0
7 bstart = 0
8 bstart = 0
8 afinish = a.length-1
9 afinish = a.length-1
9 bfinish = b.length-1
10 bfinish = b.length-1
10 mvector = []
11 mvector = []
11
12
12 # First we prune off any common elements at the beginning
13 # First we prune off any common elements at the beginning
13 while (astart <= afinish && bstart <= afinish && a[astart] == b[bstart])
14 while (astart <= afinish && bstart <= afinish && a[astart] == b[bstart])
14 mvector[astart] = bstart
15 mvector[astart] = bstart
15 astart += 1
16 astart += 1
16 bstart += 1
17 bstart += 1
17 end
18 end
18
19
19 # now the end
20 # now the end
20 while (astart <= afinish && bstart <= bfinish && a[afinish] == b[bfinish])
21 while (astart <= afinish && bstart <= bfinish && a[afinish] == b[bfinish])
21 mvector[afinish] = bfinish
22 mvector[afinish] = bfinish
22 afinish -= 1
23 afinish -= 1
23 bfinish -= 1
24 bfinish -= 1
24 end
25 end
25
26
26 bmatches = b.reverse_hash(bstart..bfinish)
27 bmatches = b.reverse_hash(bstart..bfinish)
27 thresh = []
28 thresh = []
28 links = []
29 links = []
29
30
30 (astart..afinish).each { |aindex|
31 (astart..afinish).each { |aindex|
31 aelem = a[aindex]
32 aelem = a[aindex]
32 next unless bmatches.has_key? aelem
33 next unless bmatches.has_key? aelem
33 k = nil
34 k = nil
34 bmatches[aelem].reverse.each { |bindex|
35 bmatches[aelem].reverse.each { |bindex|
35 if k && (thresh[k] > bindex) && (thresh[k-1] < bindex)
36 if k && (thresh[k] > bindex) && (thresh[k-1] < bindex)
36 thresh[k] = bindex
37 thresh[k] = bindex
37 else
38 else
38 k = thresh.replacenextlarger(bindex, k)
39 k = thresh.replacenextlarger(bindex, k)
39 end
40 end
40 links[k] = [ (k==0) ? nil : links[k-1], aindex, bindex ] if k
41 links[k] = [ (k==0) ? nil : links[k-1], aindex, bindex ] if k
42 }
41 }
43 }
42 }
43
44
44 if !thresh.empty?
45 if !thresh.empty?
45 link = links[thresh.length-1]
46 link = links[thresh.length-1]
46 while link
47 while link
47 mvector[link[1]] = link[2]
48 mvector[link[1]] = link[2]
48 link = link[0]
49 link = link[0]
50 end
49 end
51 end
50 end
51
52
52 return mvector
53 return mvector
53 end
54
55 def makediff(a, b)
56 mvector = Diff.lcs(a, b)
57 ai = bi = 0
58 while ai < mvector.length
59 bline = mvector[ai]
60 if bline
61 while bi < bline
62 discardb(bi, b[bi])
63 bi += 1
64 end
65 match(ai, bi)
66 bi += 1
67 else
68 discarda(ai, a[ai])
69 end
70 ai += 1
71 end
72 while ai < a.length
73 discarda(ai, a[ai])
74 ai += 1
75 end
54 end
76 while bi < b.length
55
56 def makediff(a, b)
57 mvector = Diff.lcs(a, b)
58 ai = bi = 0
59 while ai < mvector.length
60 bline = mvector[ai]
61 if bline
62 while bi < bline
77 discardb(bi, b[bi])
63 discardb(bi, b[bi])
78 bi += 1
64 bi += 1
79 end
65 end
80 match(ai, bi)
66 match(ai, bi)
81 1
67 bi += 1
82 end
68 else
83
69 discarda(ai, a[ai])
84 def compactdiffs
70 end
85 diffs = []
71 ai += 1
86 @diffs.each { |df|
87 i = 0
88 curdiff = []
89 while i < df.length
90 whot = df[i][0]
91 s = @isstring ? df[i][2].chr : [df[i][2]]
92 p = df[i][1]
93 last = df[i][1]
94 i += 1
95 while df[i] && df[i][0] == whot && df[i][1] == last+1
96 s << df[i][2]
97 last = df[i][1]
98 i += 1
99 end
100 curdiff.push [whot, p, s]
101 end
72 end
102 diffs.push curdiff
73 while ai < a.length
103 }
74 discarda(ai, a[ai])
104 return diffs
75 ai += 1
105 end
76 end
77 while bi < b.length
78 discardb(bi, b[bi])
79 bi += 1
80 end
81 match(ai, bi)
82 1
83 end
106
84
107 attr_reader :diffs, :difftype
85 def compactdiffs
86 diffs = []
87 @diffs.each { |df|
88 i = 0
89 curdiff = []
90 while i < df.length
91 whot = df[i][0]
92 s = @isstring ? df[i][2].chr : [df[i][2]]
93 p = df[i][1]
94 last = df[i][1]
95 i += 1
96 while df[i] && df[i][0] == whot && df[i][1] == last+1
97 s << df[i][2]
98 last = df[i][1]
99 i += 1
100 end
101 curdiff.push [whot, p, s]
102 end
103 diffs.push curdiff
104 }
105 return diffs
106 end
108
107
109 def initialize(diffs_or_a, b = nil, isstring = nil)
108 attr_reader :diffs, :difftype
110 if b.nil?
109
111 @diffs = diffs_or_a
110 def initialize(diffs_or_a, b = nil, isstring = nil)
112 @isstring = isstring
111 if b.nil?
113 else
112 @diffs = diffs_or_a
114 @diffs = []
113 @isstring = isstring
114 else
115 @diffs = []
116 @curdiffs = []
117 makediff(diffs_or_a, b)
118 @difftype = diffs_or_a.class
119 end
120 end
121
122 def match(ai, bi)
123 @diffs.push @curdiffs unless @curdiffs.empty?
115 @curdiffs = []
124 @curdiffs = []
116 makediff(diffs_or_a, b)
117 @difftype = diffs_or_a.class
118 end
125 end
119 end
120
121 def match(ai, bi)
122 @diffs.push @curdiffs unless @curdiffs.empty?
123 @curdiffs = []
124 end
125
126
126 def discarda(i, elem)
127 def discarda(i, elem)
127 @curdiffs.push ['-', i, elem]
128 @curdiffs.push ['-', i, elem]
128 end
129 end
129
130
130 def discardb(i, elem)
131 def discardb(i, elem)
131 @curdiffs.push ['+', i, elem]
132 @curdiffs.push ['+', i, elem]
132 end
133 end
133
134
134 def compact
135 def compact
135 return Diff.new(compactdiffs)
136 return Diff.new(compactdiffs)
136 end
137 end
137
138
138 def compact!
139 def compact!
139 @diffs = compactdiffs
140 @diffs = compactdiffs
140 end
141 end
141
142
142 def inspect
143 def inspect
143 @diffs.inspect
144 @diffs.inspect
144 end
145 end
145
146
147 end
146 end
148 end
147
149
148 module Diffable
150 module Diffable
149 def diff(b)
151 def diff(b)
150 Diff.new(self, b)
152 RedmineDiff::Diff.new(self, b)
151 end
153 end
152
154
153 # Create a hash that maps elements of the array to arrays of indices
155 # Create a hash that maps elements of the array to arrays of indices
@@ -158,9 +160,9 module Diffable
158 range.each { |i|
160 range.each { |i|
159 elem = self[i]
161 elem = self[i]
160 if revmap.has_key? elem
162 if revmap.has_key? elem
161 revmap[elem].push i
163 revmap[elem].push i
162 else
164 else
163 revmap[elem] = [i]
165 revmap[elem] = [i]
164 end
166 end
165 }
167 }
166 return revmap
168 return revmap
@@ -179,9 +181,9 module Diffable
179 found = self[index]
181 found = self[index]
180 return nil if value == found
182 return nil if value == found
181 if value > found
183 if value > found
182 low = index + 1
184 low = index + 1
183 else
185 else
184 high = index
186 high = index
185 end
187 end
186 end
188 end
187
189
@@ -204,25 +206,25 module Diffable
204 bi = 0
206 bi = 0
205 diff.diffs.each { |d|
207 diff.diffs.each { |d|
206 d.each { |mod|
208 d.each { |mod|
207 case mod[0]
209 case mod[0]
208 when '-'
210 when '-'
209 while ai < mod[1]
211 while ai < mod[1]
210 newary << self[ai]
212 newary << self[ai]
211 ai += 1
213 ai += 1
212 bi += 1
214 bi += 1
213 end
215 end
214 ai += 1
216 ai += 1
215 when '+'
217 when '+'
216 while bi < mod[1]
218 while bi < mod[1]
217 newary << self[ai]
219 newary << self[ai]
218 ai += 1
220 ai += 1
219 bi += 1
221 bi += 1
220 end
222 end
221 newary << mod[2]
223 newary << mod[2]
222 bi += 1
224 bi += 1
223 else
225 else
224 raise "Unknown diff action"
226 raise "Unknown diff action"
225 end
227 end
226 }
228 }
227 }
229 }
228 while ai < self.length
230 while ai < self.length
@@ -243,38 +245,38 class String
243 end
245 end
244
246
245 =begin
247 =begin
246 = Diff
248 = Diff
247 (({diff.rb})) - computes the differences between two arrays or
249 (({diff.rb})) - computes the differences between two arrays or
248 strings. Copyright (C) 2001 Lars Christensen
250 strings. Copyright (C) 2001 Lars Christensen
249
251
250 == Synopsis
252 == Synopsis
251
253
252 diff = Diff.new(a, b)
254 diff = Diff.new(a, b)
253 b = a.patch(diff)
255 b = a.patch(diff)
254
256
255 == Class Diff
257 == Class Diff
256 === Class Methods
258 === Class Methods
257 --- Diff.new(a, b)
259 --- Diff.new(a, b)
258 --- a.diff(b)
260 --- a.diff(b)
259 Creates a Diff object which represent the differences between
261 Creates a Diff object which represent the differences between
260 ((|a|)) and ((|b|)). ((|a|)) and ((|b|)) can be either be arrays
262 ((|a|)) and ((|b|)). ((|a|)) and ((|b|)) can be either be arrays
261 of any objects, strings, or object of any class that include
263 of any objects, strings, or object of any class that include
262 module ((|Diffable|))
264 module ((|Diffable|))
263
265
264 == Module Diffable
266 == Module Diffable
265 The module ((|Diffable|)) is intended to be included in any class for
267 The module ((|Diffable|)) is intended to be included in any class for
266 which differences are to be computed. Diffable is included into String
268 which differences are to be computed. Diffable is included into String
267 and Array when (({diff.rb})) is (({require}))'d.
269 and Array when (({diff.rb})) is (({require}))'d.
268
270
269 Classes including Diffable should implement (({[]})) to get element at
271 Classes including Diffable should implement (({[]})) to get element at
270 integer indices, (({<<})) to append elements to the object and
272 integer indices, (({<<})) to append elements to the object and
271 (({ClassName#new})) should accept 0 arguments to create a new empty
273 (({ClassName#new})) should accept 0 arguments to create a new empty
272 object.
274 object.
273
275
274 === Instance Methods
276 === Instance Methods
275 --- Diffable#patch(diff)
277 --- Diffable#patch(diff)
276 Applies the differences from ((|diff|)) to the object ((|obj|))
278 Applies the differences from ((|diff|)) to the object ((|obj|))
277 and return the result. ((|obj|)) is not changed. ((|obj|)) and
279 and return the result. ((|obj|)) is not changed. ((|obj|)) and
278 can be either an array or a string, but must match the object
280 can be either an array or a string, but must match the object
279 from which the ((|diff|)) was created.
281 from which the ((|diff|)) was created.
280 =end
282 =end
@@ -100,6 +100,18 module Redmine
100 def entries(path=nil, identifier=nil)
100 def entries(path=nil, identifier=nil)
101 return nil
101 return nil
102 end
102 end
103
104 def branches
105 return nil
106 end
107
108 def tags
109 return nil
110 end
111
112 def default_branch
113 return nil
114 end
103
115
104 def properties(path, identifier=nil)
116 def properties(path, identifier=nil)
105 return nil
117 return nil
@@ -260,6 +272,7 module Redmine
260
272
261 class Revision
273 class Revision
262 attr_accessor :identifier, :scmid, :name, :author, :time, :message, :paths, :revision, :branch
274 attr_accessor :identifier, :scmid, :name, :author, :time, :message, :paths, :revision, :branch
275
263 def initialize(attributes={})
276 def initialize(attributes={})
264 self.identifier = attributes[:identifier]
277 self.identifier = attributes[:identifier]
265 self.scmid = attributes[:scmid]
278 self.scmid = attributes[:scmid]
@@ -271,7 +284,25 module Redmine
271 self.revision = attributes[:revision]
284 self.revision = attributes[:revision]
272 self.branch = attributes[:branch]
285 self.branch = attributes[:branch]
273 end
286 end
274
287
288 def save(repo)
289 if repo.changesets.find_by_scmid(scmid.to_s).nil?
290 changeset = Changeset.create!(
291 :repository => repo,
292 :revision => identifier,
293 :scmid => scmid,
294 :committer => author,
295 :committed_on => time,
296 :comments => message)
297
298 paths.each do |file|
299 Change.create!(
300 :changeset => changeset,
301 :action => file[:action],
302 :path => file[:path])
303 end
304 end
305 end
275 end
306 end
276
307
277 class Annotate
308 class Annotate
@@ -21,90 +21,38 module Redmine
21 module Scm
21 module Scm
22 module Adapters
22 module Adapters
23 class GitAdapter < AbstractAdapter
23 class GitAdapter < AbstractAdapter
24
25 # Git executable name
24 # Git executable name
26 GIT_BIN = "git"
25 GIT_BIN = "git"
27
26
28 # Get the revision of a particuliar file
27 def info
29 def get_rev (rev,path)
28 begin
30
29 Info.new(:root_url => url, :lastrev => lastrev('',nil))
31 if rev != 'latest' && !rev.nil?
30 rescue
32 cmd="#{GIT_BIN} --git-dir #{target('')} show --date=iso --pretty=fuller #{shell_quote rev} -- #{shell_quote path}"
31 nil
33 else
34 @branch ||= shellout("#{GIT_BIN} --git-dir #{target('')} branch") { |io| io.grep(/\*/)[0].strip.match(/\* (.*)/)[1] }
35 cmd="#{GIT_BIN} --git-dir #{target('')} log --date=iso --pretty=fuller -1 #{@branch} -- #{shell_quote path}"
36 end
32 end
37 rev=[]
33 end
38 i=0
39 shellout(cmd) do |io|
40 files=[]
41 changeset = {}
42 parsing_descr = 0 #0: not parsing desc or files, 1: parsing desc, 2: parsing files
43
34
35 def branches
36 branches = []
37 cmd = "#{GIT_BIN} --git-dir #{target('')} branch"
38 shellout(cmd) do |io|
44 io.each_line do |line|
39 io.each_line do |line|
45 if line =~ /^commit ([0-9a-f]{40})$/
40 branches << line.match('\s*\*?\s*(.*)$')[1]
46 key = "commit"
41 end
47 value = $1
48 if (parsing_descr == 1 || parsing_descr == 2)
49 parsing_descr = 0
50 rev = Revision.new({:identifier => changeset[:commit],
51 :scmid => changeset[:commit],
52 :author => changeset[:author],
53 :time => Time.parse(changeset[:date]),
54 :message => changeset[:description],
55 :paths => files
56 })
57 changeset = {}
58 files = []
59 end
60 changeset[:commit] = $1
61 elsif (parsing_descr == 0) && line =~ /^(\w+):\s*(.*)$/
62 key = $1
63 value = $2
64 if key == "Author"
65 changeset[:author] = value
66 elsif key == "CommitDate"
67 changeset[:date] = value
68 end
69 elsif (parsing_descr == 0) && line.chomp.to_s == ""
70 parsing_descr = 1
71 changeset[:description] = ""
72 elsif (parsing_descr == 1 || parsing_descr == 2) && line =~ /^:\d+\s+\d+\s+[0-9a-f.]+\s+[0-9a-f.]+\s+(\w)\s+(.+)$/
73 parsing_descr = 2
74 fileaction = $1
75 filepath = $2
76 files << {:action => fileaction, :path => filepath}
77 elsif (parsing_descr == 1) && line.chomp.to_s == ""
78 parsing_descr = 2
79 elsif (parsing_descr == 1)
80 changeset[:description] << line
81 end
82 end
83 rev = Revision.new({:identifier => changeset[:commit],
84 :scmid => changeset[:commit],
85 :author => changeset[:author],
86 :time => (changeset[:date] ? Time.parse(changeset[:date]) : nil),
87 :message => changeset[:description],
88 :paths => files
89 })
90
91 end
42 end
92
43 branches.sort!
93 get_rev('latest',path) if rev == []
94
95 return nil if $? && $?.exitstatus != 0
96 return rev
97 end
44 end
98
45
99 def info
46 def tags
100 revs = revisions(url,nil,nil,{:limit => 1})
47 tags = []
101 if revs && revs.any?
48 cmd = "#{GIT_BIN} --git-dir #{target('')} tag"
102 Info.new(:root_url => url, :lastrev => revs.first)
49 shellout(cmd) do |io|
103 else
50 io.readlines.sort!.map{|t| t.strip}
104 nil
105 end
51 end
106 rescue Errno::ENOENT => e
52 end
107 return nil
53
54 def default_branch
55 branches.include?('master') ? 'master' : branches.first
108 end
56 end
109
57
110 def entries(path=nil, identifier=nil)
58 def entries(path=nil, identifier=nil)
@@ -121,27 +69,63 module Redmine
121 sha = $2
69 sha = $2
122 size = $3
70 size = $3
123 name = $4
71 name = $4
72 full_path = path.empty? ? name : "#{path}/#{name}"
124 entries << Entry.new({:name => name,
73 entries << Entry.new({:name => name,
125 :path => (path.empty? ? name : "#{path}/#{name}"),
74 :path => full_path,
126 :kind => ((type == "tree") ? 'dir' : 'file'),
75 :kind => (type == "tree") ? 'dir' : 'file',
127 :size => ((type == "tree") ? nil : size),
76 :size => (type == "tree") ? nil : size,
128 :lastrev => get_rev(identifier,(path.empty? ? name : "#{path}/#{name}"))
77 :lastrev => lastrev(full_path,identifier)
129
78 }) unless entries.detect{|entry| entry.name == name}
130 }) unless entries.detect{|entry| entry.name == name}
131 end
79 end
132 end
80 end
133 end
81 end
134 return nil if $? && $?.exitstatus != 0
82 return nil if $? && $?.exitstatus != 0
135 entries.sort_by_name
83 entries.sort_by_name
136 end
84 end
137
85
86 def lastrev(path,rev)
87 return nil if path.nil?
88 cmd = "#{GIT_BIN} --git-dir #{target('')} log --pretty=fuller --no-merges -n 1 "
89 cmd << " #{shell_quote rev} " if rev
90 cmd << "-- #{path} " unless path.empty?
91 shellout(cmd) do |io|
92 begin
93 id = io.gets.split[1]
94 author = io.gets.match('Author:\s+(.*)$')[1]
95 2.times { io.gets }
96 time = io.gets.match('CommitDate:\s+(.*)$')[1]
97
98 Revision.new({
99 :identifier => id,
100 :scmid => id,
101 :author => author,
102 :time => time,
103 :message => nil,
104 :paths => nil
105 })
106 rescue NoMethodError => e
107 logger.error("The revision '#{path}' has a wrong format")
108 return nil
109 end
110 end
111 end
112
113 def num_revisions
114 cmd = "#{GIT_BIN} --git-dir #{target('')} log --all --pretty=format:'' | wc -l"
115 shellout(cmd) {|io| io.gets.chomp.to_i + 1}
116 end
117
138 def revisions(path, identifier_from, identifier_to, options={})
118 def revisions(path, identifier_from, identifier_to, options={})
139 revisions = Revisions.new
119 revisions = Revisions.new
140 cmd = "#{GIT_BIN} --git-dir #{target('')} log --raw --date=iso --pretty=fuller"
120
121 cmd = "#{GIT_BIN} --git-dir #{target('')} log --find-copies-harder --raw --date=iso --pretty=fuller"
141 cmd << " --reverse" if options[:reverse]
122 cmd << " --reverse" if options[:reverse]
142 cmd << " -n #{options[:limit].to_i} " if (!options.nil?) && options[:limit]
123 cmd << " --all" if options[:all]
124 cmd << " -n #{options[:limit]} " if options[:limit]
143 cmd << " #{shell_quote(identifier_from + '..')} " if identifier_from
125 cmd << " #{shell_quote(identifier_from + '..')} " if identifier_from
144 cmd << " #{shell_quote identifier_to} " if identifier_to
126 cmd << " #{shell_quote identifier_to} " if identifier_to
127 cmd << " -- #{path}" if path && !path.empty?
128
145 shellout(cmd) do |io|
129 shellout(cmd) do |io|
146 files=[]
130 files=[]
147 changeset = {}
131 changeset = {}
@@ -154,13 +138,14 module Redmine
154 value = $1
138 value = $1
155 if (parsing_descr == 1 || parsing_descr == 2)
139 if (parsing_descr == 1 || parsing_descr == 2)
156 parsing_descr = 0
140 parsing_descr = 0
157 revision = Revision.new({:identifier => changeset[:commit],
141 revision = Revision.new({
158 :scmid => changeset[:commit],
142 :identifier => changeset[:commit],
159 :author => changeset[:author],
143 :scmid => changeset[:commit],
160 :time => Time.parse(changeset[:date]),
144 :author => changeset[:author],
161 :message => changeset[:description],
145 :time => Time.parse(changeset[:date]),
162 :paths => files
146 :message => changeset[:description],
163 })
147 :paths => files
148 })
164 if block_given?
149 if block_given?
165 yield revision
150 yield revision
166 else
151 else
@@ -182,26 +167,35 module Redmine
182 elsif (parsing_descr == 0) && line.chomp.to_s == ""
167 elsif (parsing_descr == 0) && line.chomp.to_s == ""
183 parsing_descr = 1
168 parsing_descr = 1
184 changeset[:description] = ""
169 changeset[:description] = ""
185 elsif (parsing_descr == 1 || parsing_descr == 2) && line =~ /^:\d+\s+\d+\s+[0-9a-f.]+\s+[0-9a-f.]+\s+(\w)\s+(.+)$/
170 elsif (parsing_descr == 1 || parsing_descr == 2) \
171 && line =~ /^:\d+\s+\d+\s+[0-9a-f.]+\s+[0-9a-f.]+\s+(\w)\s+(.+)$/
186 parsing_descr = 2
172 parsing_descr = 2
187 fileaction = $1
173 fileaction = $1
188 filepath = $2
174 filepath = $2
189 files << {:action => fileaction, :path => filepath}
175 files << {:action => fileaction, :path => filepath}
176 elsif (parsing_descr == 1 || parsing_descr == 2) \
177 && line =~ /^:\d+\s+\d+\s+[0-9a-f.]+\s+[0-9a-f.]+\s+(\w)\d+\s+(\S+)\s+(.+)$/
178 parsing_descr = 2
179 fileaction = $1
180 filepath = $3
181 files << {:action => fileaction, :path => filepath}
190 elsif (parsing_descr == 1) && line.chomp.to_s == ""
182 elsif (parsing_descr == 1) && line.chomp.to_s == ""
191 parsing_descr = 2
183 parsing_descr = 2
192 elsif (parsing_descr == 1)
184 elsif (parsing_descr == 1)
193 changeset[:description] << line[4..-1]
185 changeset[:description] << line[4..-1]
194 end
186 end
195 end
187 end
196
188
197 if changeset[:commit]
189 if changeset[:commit]
198 revision = Revision.new({:identifier => changeset[:commit],
190 revision = Revision.new({
199 :scmid => changeset[:commit],
191 :identifier => changeset[:commit],
200 :author => changeset[:author],
192 :scmid => changeset[:commit],
201 :time => Time.parse(changeset[:date]),
193 :author => changeset[:author],
202 :message => changeset[:description],
194 :time => Time.parse(changeset[:date]),
203 :paths => files
195 :message => changeset[:description],
204 })
196 :paths => files
197 })
198
205 if block_given?
199 if block_given?
206 yield revision
200 yield revision
207 else
201 else
@@ -213,15 +207,16 module Redmine
213 return nil if $? && $?.exitstatus != 0
207 return nil if $? && $?.exitstatus != 0
214 revisions
208 revisions
215 end
209 end
216
210
217 def diff(path, identifier_from, identifier_to=nil)
211 def diff(path, identifier_from, identifier_to=nil)
218 path ||= ''
212 path ||= ''
219 if !identifier_to
213
220 identifier_to = nil
214 if identifier_to
215 cmd = "#{GIT_BIN} --git-dir #{target('')} diff #{shell_quote identifier_to} #{shell_quote identifier_from}"
216 else
217 cmd = "#{GIT_BIN} --git-dir #{target('')} show #{shell_quote identifier_from}"
221 end
218 end
222
219
223 cmd = "#{GIT_BIN} --git-dir #{target('')} show #{shell_quote identifier_from}" if identifier_to.nil?
224 cmd = "#{GIT_BIN} --git-dir #{target('')} diff #{shell_quote identifier_to} #{shell_quote identifier_from}" if !identifier_to.nil?
225 cmd << " -- #{shell_quote path}" unless path.empty?
220 cmd << " -- #{shell_quote path}" unless path.empty?
226 diff = []
221 diff = []
227 shellout(cmd) do |io|
222 shellout(cmd) do |io|
@@ -265,6 +260,4 module Redmine
265 end
260 end
266 end
261 end
267 end
262 end
268
269 end
263 end
270
@@ -181,7 +181,7 div.square {
181 width: .6em; height: .6em;
181 width: .6em; height: .6em;
182 }
182 }
183 .contextual {float:right; white-space: nowrap; line-height:1.4em;margin-top:5px; padding-left: 10px; font-size:0.9em;}
183 .contextual {float:right; white-space: nowrap; line-height:1.4em;margin-top:5px; padding-left: 10px; font-size:0.9em;}
184 .contextual input {font-size:0.9em;}
184 .contextual input,select {font-size:0.9em;}
185 .message .contextual { margin-top: 0; }
185 .message .contextual { margin-top: 0; }
186
186
187 .splitcontentleft{float:left; width:49%;}
187 .splitcontentleft{float:left; width:49%;}
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
@@ -45,9 +45,9 class RepositoriesBazaarControllerTest < Test::Unit::TestCase
45 end
45 end
46
46
47 def test_browse_root
47 def test_browse_root
48 get :browse, :id => 3
48 get :show, :id => 3
49 assert_response :success
49 assert_response :success
50 assert_template 'browse'
50 assert_template 'show'
51 assert_not_nil assigns(:entries)
51 assert_not_nil assigns(:entries)
52 assert_equal 2, assigns(:entries).size
52 assert_equal 2, assigns(:entries).size
53 assert assigns(:entries).detect {|e| e.name == 'directory' && e.kind == 'dir'}
53 assert assigns(:entries).detect {|e| e.name == 'directory' && e.kind == 'dir'}
@@ -55,9 +55,9 class RepositoriesBazaarControllerTest < Test::Unit::TestCase
55 end
55 end
56
56
57 def test_browse_directory
57 def test_browse_directory
58 get :browse, :id => 3, :path => ['directory']
58 get :show, :id => 3, :path => ['directory']
59 assert_response :success
59 assert_response :success
60 assert_template 'browse'
60 assert_template 'show'
61 assert_not_nil assigns(:entries)
61 assert_not_nil assigns(:entries)
62 assert_equal ['doc-ls.txt', 'document.txt', 'edit.png'], assigns(:entries).collect(&:name)
62 assert_equal ['doc-ls.txt', 'document.txt', 'edit.png'], assigns(:entries).collect(&:name)
63 entry = assigns(:entries).detect {|e| e.name == 'edit.png'}
63 entry = assigns(:entries).detect {|e| e.name == 'edit.png'}
@@ -67,9 +67,9 class RepositoriesBazaarControllerTest < Test::Unit::TestCase
67 end
67 end
68
68
69 def test_browse_at_given_revision
69 def test_browse_at_given_revision
70 get :browse, :id => 3, :path => [], :rev => 3
70 get :show, :id => 3, :path => [], :rev => 3
71 assert_response :success
71 assert_response :success
72 assert_template 'browse'
72 assert_template 'show'
73 assert_not_nil assigns(:entries)
73 assert_not_nil assigns(:entries)
74 assert_equal ['directory', 'doc-deleted.txt', 'doc-ls.txt', 'doc-mkdir.txt'], assigns(:entries).collect(&:name)
74 assert_equal ['directory', 'doc-deleted.txt', 'doc-ls.txt', 'doc-mkdir.txt'], assigns(:entries).collect(&:name)
75 end
75 end
@@ -102,7 +102,7 class RepositoriesBazaarControllerTest < Test::Unit::TestCase
102 def test_directory_entry
102 def test_directory_entry
103 get :entry, :id => 3, :path => ['directory']
103 get :entry, :id => 3, :path => ['directory']
104 assert_response :success
104 assert_response :success
105 assert_template 'browse'
105 assert_template 'show'
106 assert_not_nil assigns(:entry)
106 assert_not_nil assigns(:entry)
107 assert_equal 'directory', assigns(:entry).name
107 assert_equal 'directory', assigns(:entry).name
108 end
108 end
@@ -51,9 +51,9 class RepositoriesCvsControllerTest < Test::Unit::TestCase
51 end
51 end
52
52
53 def test_browse_root
53 def test_browse_root
54 get :browse, :id => 1
54 get :show, :id => 1
55 assert_response :success
55 assert_response :success
56 assert_template 'browse'
56 assert_template 'show'
57 assert_not_nil assigns(:entries)
57 assert_not_nil assigns(:entries)
58 assert_equal 3, assigns(:entries).size
58 assert_equal 3, assigns(:entries).size
59
59
@@ -65,9 +65,9 class RepositoriesCvsControllerTest < Test::Unit::TestCase
65 end
65 end
66
66
67 def test_browse_directory
67 def test_browse_directory
68 get :browse, :id => 1, :path => ['images']
68 get :show, :id => 1, :path => ['images']
69 assert_response :success
69 assert_response :success
70 assert_template 'browse'
70 assert_template 'show'
71 assert_not_nil assigns(:entries)
71 assert_not_nil assigns(:entries)
72 assert_equal ['add.png', 'delete.png', 'edit.png'], assigns(:entries).collect(&:name)
72 assert_equal ['add.png', 'delete.png', 'edit.png'], assigns(:entries).collect(&:name)
73 entry = assigns(:entries).detect {|e| e.name == 'edit.png'}
73 entry = assigns(:entries).detect {|e| e.name == 'edit.png'}
@@ -78,9 +78,9 class RepositoriesCvsControllerTest < Test::Unit::TestCase
78
78
79 def test_browse_at_given_revision
79 def test_browse_at_given_revision
80 Project.find(1).repository.fetch_changesets
80 Project.find(1).repository.fetch_changesets
81 get :browse, :id => 1, :path => ['images'], :rev => 1
81 get :show, :id => 1, :path => ['images'], :rev => 1
82 assert_response :success
82 assert_response :success
83 assert_template 'browse'
83 assert_template 'show'
84 assert_not_nil assigns(:entries)
84 assert_not_nil assigns(:entries)
85 assert_equal ['delete.png', 'edit.png'], assigns(:entries).collect(&:name)
85 assert_equal ['delete.png', 'edit.png'], assigns(:entries).collect(&:name)
86 end
86 end
@@ -118,7 +118,7 class RepositoriesCvsControllerTest < Test::Unit::TestCase
118 def test_directory_entry
118 def test_directory_entry
119 get :entry, :id => 1, :path => ['sources']
119 get :entry, :id => 1, :path => ['sources']
120 assert_response :success
120 assert_response :success
121 assert_template 'browse'
121 assert_template 'show'
122 assert_not_nil assigns(:entry)
122 assert_not_nil assigns(:entry)
123 assert_equal 'sources', assigns(:entry).name
123 assert_equal 'sources', assigns(:entry).name
124 end
124 end
@@ -45,9 +45,9 class RepositoriesDarcsControllerTest < Test::Unit::TestCase
45 end
45 end
46
46
47 def test_browse_root
47 def test_browse_root
48 get :browse, :id => 3
48 get :show, :id => 3
49 assert_response :success
49 assert_response :success
50 assert_template 'browse'
50 assert_template 'show'
51 assert_not_nil assigns(:entries)
51 assert_not_nil assigns(:entries)
52 assert_equal 3, assigns(:entries).size
52 assert_equal 3, assigns(:entries).size
53 assert assigns(:entries).detect {|e| e.name == 'images' && e.kind == 'dir'}
53 assert assigns(:entries).detect {|e| e.name == 'images' && e.kind == 'dir'}
@@ -56,9 +56,9 class RepositoriesDarcsControllerTest < Test::Unit::TestCase
56 end
56 end
57
57
58 def test_browse_directory
58 def test_browse_directory
59 get :browse, :id => 3, :path => ['images']
59 get :show, :id => 3, :path => ['images']
60 assert_response :success
60 assert_response :success
61 assert_template 'browse'
61 assert_template 'show'
62 assert_not_nil assigns(:entries)
62 assert_not_nil assigns(:entries)
63 assert_equal ['delete.png', 'edit.png'], assigns(:entries).collect(&:name)
63 assert_equal ['delete.png', 'edit.png'], assigns(:entries).collect(&:name)
64 entry = assigns(:entries).detect {|e| e.name == 'edit.png'}
64 entry = assigns(:entries).detect {|e| e.name == 'edit.png'}
@@ -69,9 +69,9 class RepositoriesDarcsControllerTest < Test::Unit::TestCase
69
69
70 def test_browse_at_given_revision
70 def test_browse_at_given_revision
71 Project.find(3).repository.fetch_changesets
71 Project.find(3).repository.fetch_changesets
72 get :browse, :id => 3, :path => ['images'], :rev => 1
72 get :show, :id => 3, :path => ['images'], :rev => 1
73 assert_response :success
73 assert_response :success
74 assert_template 'browse'
74 assert_template 'show'
75 assert_not_nil assigns(:entries)
75 assert_not_nil assigns(:entries)
76 assert_equal ['delete.png'], assigns(:entries).collect(&:name)
76 assert_equal ['delete.png'], assigns(:entries).collect(&:name)
77 end
77 end
@@ -46,22 +46,37 class RepositoriesGitControllerTest < Test::Unit::TestCase
46 end
46 end
47
47
48 def test_browse_root
48 def test_browse_root
49 get :browse, :id => 3
49 get :show, :id => 3
50 assert_response :success
50 assert_response :success
51 assert_template 'browse'
51 assert_template 'show'
52 assert_not_nil assigns(:entries)
52 assert_not_nil assigns(:entries)
53 assert_equal 3, assigns(:entries).size
53 assert_equal 6, assigns(:entries).size
54 assert assigns(:entries).detect {|e| e.name == 'images' && e.kind == 'dir'}
54 assert assigns(:entries).detect {|e| e.name == 'images' && e.kind == 'dir'}
55 assert assigns(:entries).detect {|e| e.name == 'sources' && e.kind == 'dir'}
55 assert assigns(:entries).detect {|e| e.name == 'sources' && e.kind == 'dir'}
56 assert assigns(:entries).detect {|e| e.name == 'README' && e.kind == 'file'}
56 assert assigns(:entries).detect {|e| e.name == 'README' && e.kind == 'file'}
57 assert assigns(:entries).detect {|e| e.name == 'copied_README' && e.kind == 'file'}
58 assert assigns(:entries).detect {|e| e.name == 'new_file.txt' && e.kind == 'file'}
59 assert assigns(:entries).detect {|e| e.name == 'renamed_test.txt' && e.kind == 'file'}
57 end
60 end
58
61
62 def test_browse_branch
63 get :show, :id => 3, :rev => 'test_branch'
64 assert_response :success
65 assert_template 'show'
66 assert_not_nil assigns(:entries)
67 assert_equal 4, assigns(:entries).size
68 assert assigns(:entries).detect {|e| e.name == 'images' && e.kind == 'dir'}
69 assert assigns(:entries).detect {|e| e.name == 'sources' && e.kind == 'dir'}
70 assert assigns(:entries).detect {|e| e.name == 'README' && e.kind == 'file'}
71 assert assigns(:entries).detect {|e| e.name == 'test.txt' && e.kind == 'file'}
72 end
73
59 def test_browse_directory
74 def test_browse_directory
60 get :browse, :id => 3, :path => ['images']
75 get :show, :id => 3, :path => ['images']
61 assert_response :success
76 assert_response :success
62 assert_template 'browse'
77 assert_template 'show'
63 assert_not_nil assigns(:entries)
78 assert_not_nil assigns(:entries)
64 assert_equal ['delete.png', 'edit.png'], assigns(:entries).collect(&:name)
79 assert_equal ['edit.png'], assigns(:entries).collect(&:name)
65 entry = assigns(:entries).detect {|e| e.name == 'edit.png'}
80 entry = assigns(:entries).detect {|e| e.name == 'edit.png'}
66 assert_not_nil entry
81 assert_not_nil entry
67 assert_equal 'file', entry.kind
82 assert_equal 'file', entry.kind
@@ -69,9 +84,9 class RepositoriesGitControllerTest < Test::Unit::TestCase
69 end
84 end
70
85
71 def test_browse_at_given_revision
86 def test_browse_at_given_revision
72 get :browse, :id => 3, :path => ['images'], :rev => '7234cb2750b63f47bff735edc50a1c0a433c2518'
87 get :show, :id => 3, :path => ['images'], :rev => '7234cb2750b63f47bff735edc50a1c0a433c2518'
73 assert_response :success
88 assert_response :success
74 assert_template 'browse'
89 assert_template 'show'
75 assert_not_nil assigns(:entries)
90 assert_not_nil assigns(:entries)
76 assert_equal ['delete.png'], assigns(:entries).collect(&:name)
91 assert_equal ['delete.png'], assigns(:entries).collect(&:name)
77 end
92 end
@@ -89,7 +104,7 class RepositoriesGitControllerTest < Test::Unit::TestCase
89 assert_template 'entry'
104 assert_template 'entry'
90 # Line 19
105 # Line 19
91 assert_tag :tag => 'th',
106 assert_tag :tag => 'th',
92 :content => /10/,
107 :content => /11/,
93 :attributes => { :class => /line-num/ },
108 :attributes => { :class => /line-num/ },
94 :sibling => { :tag => 'td', :content => /WITHOUT ANY WARRANTY/ }
109 :sibling => { :tag => 'td', :content => /WITHOUT ANY WARRANTY/ }
95 end
110 end
@@ -104,7 +119,7 class RepositoriesGitControllerTest < Test::Unit::TestCase
104 def test_directory_entry
119 def test_directory_entry
105 get :entry, :id => 3, :path => ['sources']
120 get :entry, :id => 3, :path => ['sources']
106 assert_response :success
121 assert_response :success
107 assert_template 'browse'
122 assert_template 'show'
108 assert_not_nil assigns(:entry)
123 assert_not_nil assigns(:entry)
109 assert_equal 'sources', assigns(:entry).name
124 assert_equal 'sources', assigns(:entry).name
110 end
125 end
@@ -127,14 +142,14 class RepositoriesGitControllerTest < Test::Unit::TestCase
127 assert_response :success
142 assert_response :success
128 assert_template 'annotate'
143 assert_template 'annotate'
129 # Line 23, changeset 2f9c0091
144 # Line 23, changeset 2f9c0091
130 assert_tag :tag => 'th', :content => /23/,
145 assert_tag :tag => 'th', :content => /24/,
131 :sibling => { :tag => 'td', :child => { :tag => 'a', :content => /2f9c0091/ } },
146 :sibling => { :tag => 'td', :child => { :tag => 'a', :content => /2f9c0091/ } },
132 :sibling => { :tag => 'td', :content => /jsmith/ },
147 :sibling => { :tag => 'td', :content => /jsmith/ },
133 :sibling => { :tag => 'td', :content => /watcher =/ }
148 :sibling => { :tag => 'td', :content => /watcher =/ }
134 end
149 end
135
150
136 def test_annotate_binary_file
151 def test_annotate_binary_file
137 get :annotate, :id => 3, :path => ['images', 'delete.png']
152 get :annotate, :id => 3, :path => ['images', 'edit.png']
138 assert_response 500
153 assert_response 500
139 assert_tag :tag => 'div', :attributes => { :class => /error/ },
154 assert_tag :tag => 'div', :attributes => { :class => /error/ },
140 :content => /can not be annotated/
155 :content => /can not be annotated/
@@ -44,10 +44,10 class RepositoriesMercurialControllerTest < Test::Unit::TestCase
44 assert_not_nil assigns(:changesets)
44 assert_not_nil assigns(:changesets)
45 end
45 end
46
46
47 def test_browse_root
47 def test_show_root
48 get :browse, :id => 3
48 get :show, :id => 3
49 assert_response :success
49 assert_response :success
50 assert_template 'browse'
50 assert_template 'show'
51 assert_not_nil assigns(:entries)
51 assert_not_nil assigns(:entries)
52 assert_equal 3, assigns(:entries).size
52 assert_equal 3, assigns(:entries).size
53 assert assigns(:entries).detect {|e| e.name == 'images' && e.kind == 'dir'}
53 assert assigns(:entries).detect {|e| e.name == 'images' && e.kind == 'dir'}
@@ -55,10 +55,10 class RepositoriesMercurialControllerTest < Test::Unit::TestCase
55 assert assigns(:entries).detect {|e| e.name == 'README' && e.kind == 'file'}
55 assert assigns(:entries).detect {|e| e.name == 'README' && e.kind == 'file'}
56 end
56 end
57
57
58 def test_browse_directory
58 def test_show_directory
59 get :browse, :id => 3, :path => ['images']
59 get :show, :id => 3, :path => ['images']
60 assert_response :success
60 assert_response :success
61 assert_template 'browse'
61 assert_template 'show'
62 assert_not_nil assigns(:entries)
62 assert_not_nil assigns(:entries)
63 assert_equal ['delete.png', 'edit.png'], assigns(:entries).collect(&:name)
63 assert_equal ['delete.png', 'edit.png'], assigns(:entries).collect(&:name)
64 entry = assigns(:entries).detect {|e| e.name == 'edit.png'}
64 entry = assigns(:entries).detect {|e| e.name == 'edit.png'}
@@ -67,10 +67,10 class RepositoriesMercurialControllerTest < Test::Unit::TestCase
67 assert_equal 'images/edit.png', entry.path
67 assert_equal 'images/edit.png', entry.path
68 end
68 end
69
69
70 def test_browse_at_given_revision
70 def test_show_at_given_revision
71 get :browse, :id => 3, :path => ['images'], :rev => 0
71 get :show, :id => 3, :path => ['images'], :rev => 0
72 assert_response :success
72 assert_response :success
73 assert_template 'browse'
73 assert_template 'show'
74 assert_not_nil assigns(:entries)
74 assert_not_nil assigns(:entries)
75 assert_equal ['delete.png'], assigns(:entries).collect(&:name)
75 assert_equal ['delete.png'], assigns(:entries).collect(&:name)
76 end
76 end
@@ -103,7 +103,7 class RepositoriesMercurialControllerTest < Test::Unit::TestCase
103 def test_directory_entry
103 def test_directory_entry
104 get :entry, :id => 3, :path => ['sources']
104 get :entry, :id => 3, :path => ['sources']
105 assert_response :success
105 assert_response :success
106 assert_template 'browse'
106 assert_template 'show'
107 assert_not_nil assigns(:entry)
107 assert_not_nil assigns(:entry)
108 assert_equal 'sources', assigns(:entry).name
108 assert_equal 'sources', assigns(:entry).name
109 end
109 end
@@ -47,18 +47,18 class RepositoriesSubversionControllerTest < Test::Unit::TestCase
47 end
47 end
48
48
49 def test_browse_root
49 def test_browse_root
50 get :browse, :id => 1
50 get :show, :id => 1
51 assert_response :success
51 assert_response :success
52 assert_template 'browse'
52 assert_template 'show'
53 assert_not_nil assigns(:entries)
53 assert_not_nil assigns(:entries)
54 entry = assigns(:entries).detect {|e| e.name == 'subversion_test'}
54 entry = assigns(:entries).detect {|e| e.name == 'subversion_test'}
55 assert_equal 'dir', entry.kind
55 assert_equal 'dir', entry.kind
56 end
56 end
57
57
58 def test_browse_directory
58 def test_browse_directory
59 get :browse, :id => 1, :path => ['subversion_test']
59 get :show, :id => 1, :path => ['subversion_test']
60 assert_response :success
60 assert_response :success
61 assert_template 'browse'
61 assert_template 'show'
62 assert_not_nil assigns(:entries)
62 assert_not_nil assigns(:entries)
63 assert_equal ['folder', '.project', 'helloworld.c', 'textfile.txt'], assigns(:entries).collect(&:name)
63 assert_equal ['folder', '.project', 'helloworld.c', 'textfile.txt'], assigns(:entries).collect(&:name)
64 entry = assigns(:entries).detect {|e| e.name == 'helloworld.c'}
64 entry = assigns(:entries).detect {|e| e.name == 'helloworld.c'}
@@ -68,9 +68,9 class RepositoriesSubversionControllerTest < Test::Unit::TestCase
68 end
68 end
69
69
70 def test_browse_at_given_revision
70 def test_browse_at_given_revision
71 get :browse, :id => 1, :path => ['subversion_test'], :rev => 4
71 get :show, :id => 1, :path => ['subversion_test'], :rev => 4
72 assert_response :success
72 assert_response :success
73 assert_template 'browse'
73 assert_template 'show'
74 assert_not_nil assigns(:entries)
74 assert_not_nil assigns(:entries)
75 assert_equal ['folder', '.project', 'helloworld.c', 'helloworld.rb', 'textfile.txt'], assigns(:entries).collect(&:name)
75 assert_equal ['folder', '.project', 'helloworld.c', 'helloworld.rb', 'textfile.txt'], assigns(:entries).collect(&:name)
76 end
76 end
@@ -131,7 +131,7 class RepositoriesSubversionControllerTest < Test::Unit::TestCase
131 def test_directory_entry
131 def test_directory_entry
132 get :entry, :id => 1, :path => ['subversion_test', 'folder']
132 get :entry, :id => 1, :path => ['subversion_test', 'folder']
133 assert_response :success
133 assert_response :success
134 assert_template 'browse'
134 assert_template 'show'
135 assert_not_nil assigns(:entry)
135 assert_not_nil assigns(:entry)
136 assert_equal 'folder', assigns(:entry).name
136 assert_equal 'folder', assigns(:entry).name
137 end
137 end
@@ -34,8 +34,8 class RepositoryGitTest < Test::Unit::TestCase
34 @repository.fetch_changesets
34 @repository.fetch_changesets
35 @repository.reload
35 @repository.reload
36
36
37 assert_equal 6, @repository.changesets.count
37 assert_equal 12, @repository.changesets.count
38 assert_equal 11, @repository.changes.count
38 assert_equal 20, @repository.changes.count
39
39
40 commit = @repository.changesets.find(:first, :order => 'committed_on ASC')
40 commit = @repository.changesets.find(:first, :order => 'committed_on ASC')
41 assert_equal "Initial import.\nThe repository contains 3 files.", commit.comments
41 assert_equal "Initial import.\nThe repository contains 3 files.", commit.comments
@@ -57,10 +57,10 class RepositoryGitTest < Test::Unit::TestCase
57 # Remove the 3 latest changesets
57 # Remove the 3 latest changesets
58 @repository.changesets.find(:all, :order => 'committed_on DESC', :limit => 3).each(&:destroy)
58 @repository.changesets.find(:all, :order => 'committed_on DESC', :limit => 3).each(&:destroy)
59 @repository.reload
59 @repository.reload
60 assert_equal 3, @repository.changesets.count
60 assert_equal 9, @repository.changesets.count
61
61
62 @repository.fetch_changesets
62 @repository.fetch_changesets
63 assert_equal 6, @repository.changesets.count
63 assert_equal 12, @repository.changesets.count
64 end
64 end
65 else
65 else
66 puts "Git test repository NOT FOUND. Skipping unit tests !!!"
66 puts "Git test repository NOT FOUND. Skipping unit tests !!!"
General Comments 0
You need to be logged in to leave comments. Login now