Feature #1406 » branch_support.diff
| app/controllers/repositories_controller.rb | ||
|---|---|---|
| 64 | 64 |
redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'repository' |
| 65 | 65 |
end |
| 66 | 66 |
|
| 67 |
def show |
|
| 68 |
# check if new revisions have been committed in the repository |
|
| 69 |
@repository.fetch_changesets if Setting.autofetch_changesets? |
|
| 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 |
|
| 67 |
def show |
|
| 68 |
@repository.fetch_changesets if Setting.autofetch_changesets? && @path.empty? |
|
| 69 | ||
| 78 | 70 |
@entries = @repository.entries(@path, @rev) |
| 79 | 71 |
if request.xhr? |
| 80 | 72 |
@entries ? render(:partial => 'dir_list_content') : render(:nothing => true) |
| 81 | 73 |
else |
| 82 | 74 |
show_error_not_found and return unless @entries |
| 75 |
@changesets = @repository.latest_changesets(@path, @rev) |
|
| 83 | 76 |
@properties = @repository.properties(@path, @rev) |
| 84 |
render :action => 'browse'
|
|
| 77 |
render :action => 'show'
|
|
| 85 | 78 |
end |
| 86 | 79 |
end |
| 80 | ||
| 81 |
alias_method :browse, :show |
|
| 87 | 82 |
|
| 88 | 83 |
def changes |
| 89 | 84 |
@entry = @repository.entry(@path, @rev) |
| 90 | 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 | 87 |
@properties = @repository.properties(@path, @rev) |
| 93 | 88 |
end |
| 94 | 89 |
|
| ... | ... | |
| 135 | 130 |
end |
| 136 | 131 |
|
| 137 | 132 |
def revision |
| 138 |
@changeset = @repository.changesets.find_by_revision(@rev)
|
|
| 133 |
@changeset = @repository.changesets.find(:first, :conditions => ["revision LIKE ?", @rev + '%'])
|
|
| 139 | 134 |
raise ChangesetNotFound unless @changeset |
| 140 | 135 | |
| 141 | 136 |
respond_to do |format| |
| ... | ... | |
| 199 | 194 |
render_404 |
| 200 | 195 |
end |
| 201 | 196 |
|
| 202 |
REV_PARAM_RE = %r{^[a-f0-9]*$}
|
|
| 203 |
|
|
| 204 | 197 |
def find_repository |
| 205 | 198 |
@project = Project.find(params[:id]) |
| 206 | 199 |
@repository = @project.repository |
| 207 | 200 |
render_404 and return false unless @repository |
| 208 | 201 |
@path = params[:path].join('/') unless params[:path].nil?
|
| 209 | 202 |
@path ||= '' |
| 210 |
@rev = params[:rev] |
|
| 203 |
@rev = params[:rev].nil? || params[:rev].empty? ? @repository.default_branch : params[:rev].strip
|
|
| 211 | 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 | 205 |
rescue ActiveRecord::RecordNotFound |
| 214 | 206 |
render_404 |
| 215 | 207 |
rescue InvalidRevisionParam |
| app/models/repository.rb | ||
|---|---|---|
| 62 | 62 |
def entries(path=nil, identifier=nil) |
| 63 | 63 |
scm.entries(path, identifier) |
| 64 | 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 | 78 |
def properties(path, identifier=nil) |
| 67 | 79 |
scm.properties(path, identifier) |
| ... | ... | |
| 92 | 104 |
def latest_changeset |
| 93 | 105 |
@latest_changeset ||= changesets.find(:first) |
| 94 | 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 | 112 |
def scan_changesets_for_issue_ids |
| 97 | 113 |
self.changesets.each(&:scan_comment_for_issue_ids) |
| 98 | 114 |
end |
| 99 |
|
|
| 115 | ||
| 100 | 116 |
# Returns an array of committers usernames and associated user_id |
| 101 | 117 |
def committers |
| 102 | 118 |
@committers ||= Changeset.connection.select_rows("SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}")
|
| app/models/repository/git.rb | ||
|---|---|---|
| 29 | 29 |
'Git' |
| 30 | 30 |
end |
| 31 | 31 | |
| 32 |
def branches |
|
| 33 |
scm.branches |
|
| 34 |
end |
|
| 35 | ||
| 36 |
def tags |
|
| 37 |
scm.tags |
|
| 38 |
end |
|
| 39 | ||
| 32 | 40 |
def changesets_for_path(path, options={})
|
| 33 |
Change.find(:all, :include => {:changeset => :user},
|
|
| 34 |
:conditions => ["repository_id = ? AND path = ?", id, path], |
|
| 35 |
:order => "committed_on DESC, #{Changeset.table_name}.revision DESC",
|
|
| 36 |
:limit => options[:limit]).collect(&:changeset) |
|
| 41 |
Change.find( |
|
| 42 |
:all, |
|
| 43 |
:include => {:changeset => :user},
|
|
| 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 | 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 | 55 |
def fetch_changesets |
| 40 |
scm_info = scm.info |
|
| 41 |
if scm_info |
|
| 42 |
# latest revision found in database |
|
| 43 |
db_revision = latest_changeset ? latest_changeset.revision : nil |
|
| 44 |
# latest revision in the repository |
|
| 45 |
scm_revision = scm_info.lastrev.scmid |
|
| 56 |
# Save ourselves an expensive operation if we're already up to date |
|
| 57 |
return if scm.num_revisions == changesets.count |
|
| 58 | ||
| 59 |
revisions = scm.revisions('', nil, nil, :all => true)
|
|
| 60 |
return if revisions.nil? || revisions.empty? |
|
| 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) |
|
| 48 |
scm.revisions('', db_revision, nil, :reverse => true) do |revision|
|
|
| 49 |
if changesets.find_by_scmid(revision.scmid.to_s).nil? |
|
| 50 |
transaction do |
|
| 51 |
changeset = Changeset.create!(:repository => self, |
|
| 52 |
:revision => revision.identifier, |
|
| 53 |
:scmid => revision.scmid, |
|
| 54 |
:committer => revision.author, |
|
| 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 |
|
| 79 |
changesets.find( |
|
| 80 |
:all, |
|
| 81 |
:conditions => [ |
|
| 82 |
"scmid IN (?)", |
|
| 83 |
revisions.map!{|c| c.scmid}
|
|
| 84 |
], |
|
| 85 |
:order => 'committed_on DESC' |
|
| 86 |
) |
|
| 70 | 87 |
end |
| 71 | 88 |
end |
| app/views/repositories/_breadcrumbs.rhtml | ||
|---|---|---|
| 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)) -%> |
|
| app/views/repositories/_dir_list_content.rhtml | ||
|---|---|---|
| 4 | 4 |
<tr id="<%= tr_id %>" class="<%= params[:parent_id] %> entry <%= entry.kind %>"> |
| 5 | 5 |
<td style="padding-left: <%=18 * depth%>px;" class="filename"> |
| 6 | 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 | 8 |
:method => :get, |
| 9 | 9 |
:update => { :success => tr_id },
|
| 10 | 10 |
:position => :after, |
| ... | ... | |
| 12 | 12 |
:condition => "scmEntryClick('#{tr_id}')"%>"> </span>
|
| 13 | 13 |
<% end %> |
| 14 | 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 | 16 |
:class => (entry.is_dir? ? 'icon icon-folder' : "icon icon-file #{Redmine::MimeType.css_class_of(entry.name)}")%>
|
| 17 | 17 |
</td> |
| 18 | 18 |
<td class="size"><%= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %></td> |
| app/views/repositories/_navigation.rhtml | ||
|---|---|---|
| 1 |
<%= link_to 'root', :action => 'browse', :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 => '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 %>
|
|
| 1 |
<% content_for :header_tags do %> |
|
| 2 |
<%= javascript_include_tag 'repository_navigation' %> |
|
| 17 | 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 -%> |
|
| app/views/repositories/annotate.rhtml | ||
|---|---|---|
| 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 | 9 |
<p><%= render :partial => 'link_to_functions' %></p> |
| 4 | 10 | |
| app/views/repositories/browse.rhtml | ||
|---|---|---|
| 1 | 1 |
<div class="contextual"> |
| 2 |
<% form_tag({}, :method => :get) do %>
|
|
| 3 |
<%= l(:label_revision) %>: <%= text_field_tag 'rev', @rev, :size => 5 %> |
|
| 4 |
<% end %> |
|
| 2 |
<%= render :partial => 'navigation' %> |
|
| 5 | 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 | 7 |
<%= render :partial => 'dir_list' %> |
| 10 | 8 |
<%= render_properties(@properties) %> |
| app/views/repositories/changes.rhtml | ||
|---|---|---|
| 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 | 11 |
<p><%= render :partial => 'link_to_functions' %></p> |
| 4 | 12 | |
| app/views/repositories/entry.rhtml | ||
|---|---|---|
| 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 | 9 |
<p><%= render :partial => 'link_to_functions' %></p> |
| 4 | 10 | |
| app/views/repositories/revision.rhtml | ||
|---|---|---|
| 14 | 14 |
» |
| 15 | 15 | |
| 16 | 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 | 18 |
<%= submit_tag 'OK', :name => nil %> |
| 19 | 19 |
<% end %> |
| 20 | 20 |
</div> |
| app/views/repositories/revisions.rhtml | ||
|---|---|---|
| 1 | 1 |
<div class="contextual"> |
| 2 | 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 | 4 |
<%= submit_tag 'OK' %> |
| 5 | 5 |
<% end %> |
| 6 | 6 |
</div> |
| app/views/repositories/show.rhtml | ||
|---|---|---|
| 1 |
<div class="contextual"> |
|
| 2 | 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') -%>
|
|
| 6 |
<% form_tag({:action => 'browse', :id => @project}, :method => :get) do -%>
|
|
| 7 |
| <%= l(:label_revision) %>: <%= text_field_tag 'rev', @rev, :size => 5 %> |
|
| 8 |
<% end -%> |
|
| 9 |
<% end -%> |
|
| 3 |
<div class="contextual"> |
|
| 4 |
<%= render :partial => 'navigation' %> |
|
| 10 | 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 | 9 |
<% if !@entries.nil? && authorize_for('repositories', 'browse') %>
|
| 15 | 10 |
<%= render :partial => 'dir_list' %> |
| ... | ... | |
| 18 | 13 |
<% if !@changesets.empty? && authorize_for('repositories', 'revisions') %>
|
| 19 | 14 |
<h3><%= l(:label_latest_revision_plural) %></h3> |
| 20 | 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 | 17 |
<% content_for :header_tags do %> |
| 23 | 18 |
<%= auto_discovery_link_tag(:atom, params.merge({:format => 'atom', :action => 'revisions', :id => @project, :page => nil, :key => User.current.rss_key})) %>
|
| 24 | 19 |
<% end %> |
| config/locales/bg.yml | ||
|---|---|---|
| 798 | 798 |
mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
|
| 799 | 799 |
permission_add_project: Create project |
| 800 | 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 |
|
| config/locales/bs.yml | ||
|---|---|---|
| 831 | 831 |
mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
|
| 832 | 832 |
permission_add_project: Create project |
| 833 | 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 |
|
| config/locales/ca.yml | ||
|---|---|---|
| 801 | 801 |
mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
|
| 802 | 802 |
permission_add_project: Create project |
| 803 | 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 |
|
| config/locales/cs.yml | ||
|---|---|---|
| 804 | 804 |
mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
|
| 805 | 805 |
permission_add_project: Create project |
| 806 | 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 |
|
| config/locales/da.yml | ||
|---|---|---|
| 831 | 831 |
mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
|
| 832 | 832 |
permission_add_project: Create project |
| 833 | 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 |
|
| config/locales/de.yml | ||
|---|---|---|
| 830 | 830 |
mail_body_wiki_content_updated: "Die Wiki-Seite '{{page}}' wurde von {{author}} aktualisiert."
|
| 831 | 831 |
permission_add_project: Erstelle Projekt |
| 832 | 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 |
|
| config/locales/en.yml | ||
|---|---|---|
| 543 | 543 |
label_browse: Browse |
| 544 | 544 |
label_modification: "{{count}} change"
|
| 545 | 545 |
label_modification_plural: "{{count}} changes"
|
| 546 |
label_branch: Branch |
|
| 547 |
label_tag: Tag |
|
| 546 | 548 |
label_revision: Revision |
| 547 | 549 |
label_revision_plural: Revisions |
| 548 | 550 |
label_associated_revisions: Associated revisions |
| ... | ... | |
| 554 | 556 |
label_latest_revision: Latest revision |
| 555 | 557 |
label_latest_revision_plural: Latest revisions |
| 556 | 558 |
label_view_revisions: View revisions |
| 559 |
label_view_all_revisions: View all revisions |
|
| 557 | 560 |
label_max_size: Maximum size |
| 558 | 561 |
label_sort_highest: Move to top |
| 559 | 562 |
label_sort_higher: Move up |
| config/locales/es.yml | ||
|---|---|---|
| 851 | 851 |
mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
|
| 852 | 852 |
permission_add_project: Create project |
| 853 | 853 |
setting_new_project_user_role_id: Role given to a non-admin user who creates a project |
| 854 |
label_view_all_revisions: View all revisions |
|
| 855 |
label_tag: Tag |
|
| 856 |
label_branch: Branch |
|
| config/locales/fi.yml | ||
|---|---|---|
| 841 | 841 |
mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
|
| 842 | 842 |
permission_add_project: Create project |
| 843 | 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 |
|
| config/locales/fr.yml | ||
|---|---|---|
| 832 | 832 |
enumeration_doc_categories: Catégories des documents |
| 833 | 833 |
enumeration_activities: Activités (suivi du temps) |
| 834 | 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 |
|
| config/locales/gl.yml | ||
|---|---|---|
| 830 | 830 |
mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
|
| 831 | 831 |
permission_add_project: Create project |
| 832 | 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 |
|
| config/locales/he.yml | ||
|---|---|---|
| 813 | 813 |
mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
|
| 814 | 814 |
permission_add_project: Create project |
| 815 | 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 |
|
| config/locales/hu.yml | ||
|---|---|---|
| 836 | 836 |
mail_body_wiki_content_updated: A '{{page}}' wiki oldalt {{author}} frissítette.
|
| 837 | 837 |
permission_add_project: Projekt létrehozása |
| 838 | 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 |
|
| config/locales/it.yml | ||
|---|---|---|
| 816 | 816 |
mail_body_wiki_content_updated: La pagina '{{page}}' wiki è stata aggiornata da{{author}}.
|
| 817 | 817 |
permission_add_project: Crea progetto |
| 818 | 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 |
|
| config/locales/ja.yml | ||
|---|---|---|
| 838 | 838 |
enumeration_issue_priorities: チケットの優先度 |
| 839 | 839 |
enumeration_doc_categories: 文書カテゴリ |
| 840 | 840 |
enumeration_activities: 作業分類 (時間トラッキング) |
| 841 |
label_view_all_revisions: View all revisions |
|
| 842 |
label_tag: Tag |
|
| 843 |
label_branch: Branch |
|
| config/locales/ko.yml | ||
|---|---|---|
| 860 | 860 |
mail_body_wiki_content_updated: 위키페이지 '{{page}}' 가 {{author}}에 의하여 고쳐젔습니다.
|
| 861 | 861 |
permission_add_project: 프로젝트 생성 |
| 862 | 862 |
setting_new_project_user_role_id: 프로젝트를 만든 일반사용자에게 주어질 권한 |
| 863 |
label_view_all_revisions: View all revisions |
|
| 864 |
label_tag: Tag |
|
| 865 |
label_branch: Branch |
|
| config/locales/lt.yml | ||
|---|---|---|
| 841 | 841 |
mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
|
| 842 | 842 |
permission_add_project: Create project |
| 843 | 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 |
|
| config/locales/nl.yml | ||
|---|---|---|
| 786 | 786 |
mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
|
| 787 | 787 |
permission_add_project: Create project |
| 788 | 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 |
|
| config/locales/no.yml | ||
|---|---|---|
| 803 | 803 |
mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
|
| 804 | 804 |
permission_add_project: Create project |
| 805 | 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 |
|
| config/locales/pl.yml | ||
|---|---|---|
| 834 | 834 |
mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
|
| 835 | 835 |
permission_add_project: Create project |
| 836 | 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 |
|
| config/locales/pt-BR.yml | ||
|---|---|---|
| 836 | 836 |
mail_body_wiki_content_updated: A página wiki '{{page}}' foi atualizada por {{author}}.
|
| 837 | 837 |
permission_add_project: Criar projeto |
| 838 | 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 |
|
| config/locales/pt.yml | ||
|---|---|---|
| 822 | 822 |
mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
|
| 823 | 823 |
permission_add_project: Create project |
| 824 | 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 |
|
| config/locales/ro.yml | ||
|---|---|---|
| 801 | 801 |
mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
|
| 802 | 802 |
permission_add_project: Create project |
| 803 | 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 |
|
| config/locales/ru.yml | ||
|---|---|---|
| 928 | 928 |
mail_body_wiki_content_updated: "{{author}} обновил(а) wiki-страницу '{{page}}'."
|
| 929 | 929 |
permission_add_project: Создание проекта |
| 930 | 930 |
setting_new_project_user_role_id: Роль, назначаемая пользователю, создавшему проект |
| 931 |
label_view_all_revisions: View all revisions |
|
| 932 |
label_tag: Tag |
|
| 933 |
label_branch: Branch |
|
| config/locales/sk.yml | ||
|---|---|---|
| 802 | 802 |
label_wiki_content_updated: Wiki stránka aktualizovaná |
| 803 | 803 |
mail_body_wiki_content_updated: Wiki stránka '{{page}}' bola aktualizovaná užívateľom {{author}}.
|
| 804 | 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 |
|
| 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 |
|
| config/locales/sl.yml | ||
|---|---|---|
| 800 | 800 |
mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
|
| 801 | 801 |
permission_add_project: Create project |
| 802 | 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 |
|
| config/locales/sr.yml | ||
|---|---|---|
| 824 | 824 |
mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
|
| 825 | 825 |
permission_add_project: Create project |
| 826 | 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 |
|
| config/locales/sv.yml | ||
|---|---|---|
| 858 | 858 |
enumeration_issue_priorities: Ärendeprioriteter |
| 859 | 859 |
enumeration_doc_categories: Dokumentkategorier |
| 860 | 860 |
enumeration_activities: Aktiviteter (tidsuppföljning) |
| 861 |
label_view_all_revisions: View all revisions |
|
| 862 |
label_tag: Tag |
|
| 863 |
label_branch: Branch |
|
| config/locales/th.yml | ||
|---|---|---|
| 801 | 801 |
mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
|
| 802 | 802 |
permission_add_project: Create project |
| 803 | 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 |
|
| config/locales/tr.yml | ||
|---|---|---|
| 837 | 837 |
mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
|
| 838 | 838 |
permission_add_project: Create project |
| 839 | 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 |
|
| config/locales/uk.yml | ||
|---|---|---|
| 800 | 800 |
mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
|
| 801 | 801 |
permission_add_project: Create project |
| 802 | 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 |
|
| config/locales/vi.yml | ||
|---|---|---|
| 870 | 870 |
mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
|
| 871 | 871 |
permission_add_project: Create project |
| 872 | 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 |
|
| config/locales/zh-TW.yml | ||
|---|---|---|
| 908 | 908 |
enumeration_issue_priorities: 項目優先權 |
| 909 | 909 |
enumeration_doc_categories: 文件分類 |
| 910 | 910 |
enumeration_activities: 活動 (時間追蹤) |
| 911 |
label_view_all_revisions: View all revisions |
|
| 912 |
label_tag: Tag |
|
| 913 |
label_branch: Branch |
|
| config/locales/zh.yml | ||
|---|---|---|
| 833 | 833 |
enumeration_issue_priorities: 问题优先级 |
| 834 | 834 |
enumeration_doc_categories: 文档类别 |
| 835 | 835 |
enumeration_activities: 活动(时间跟踪) |
| 836 |
label_view_all_revisions: View all revisions |
|
| 837 |
label_tag: Tag |
|
| 838 |
label_branch: Branch |
|
| config/routes.rb | ||
|---|---|---|
| 218 | 218 |
repository_views.connect 'projects/:id/repository/revisions/:rev', :action => 'revision' |
| 219 | 219 |
repository_views.connect 'projects/:id/repository/revisions/:rev/diff', :action => 'diff' |
| 220 | 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 | 222 |
repository_views.connect 'projects/:id/repository/:action/*path' |
| 223 | 223 |
end |
| 224 | 224 |
|
| lib/diff.rb | ||
|---|---|---|
| 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 |
astart = 0 |
|
| 7 |
bstart = 0 |
|
| 8 |
afinish = a.length-1 |
|
| 9 |
bfinish = b.length-1 |
|
| 10 |
mvector = [] |
|
| 11 |
|
|
| 12 |
# First we prune off any common elements at the beginning |
|
| 13 |
while (astart <= afinish && bstart <= afinish && a[astart] == b[bstart]) |
|
| 14 |
mvector[astart] = bstart |
|
| 15 |
astart += 1 |
|
| 16 |
bstart += 1 |
|
| 17 |
end |
|
| 18 |
|
|
| 19 |
# now the end |
|
| 20 |
while (astart <= afinish && bstart <= bfinish && a[afinish] == b[bfinish]) |
|
| 21 |
mvector[afinish] = bfinish |
|
| 22 |
afinish -= 1 |
|
| 23 |
bfinish -= 1 |
|
| 24 |
end |
|
| 6 |
def Diff.lcs(a, b)
|
|
| 7 |
astart = 0
|
|
| 8 |
bstart = 0
|
|
| 9 |
afinish = a.length-1
|
|
| 10 |
bfinish = b.length-1
|
|
| 11 |
mvector = []
|
|
| 12 |
|
|
| 13 |
# First we prune off any common elements at the beginning
|
|
| 14 |
while (astart <= afinish && bstart <= afinish && a[astart] == b[bstart])
|
|
| 15 |
mvector[astart] = bstart
|
|
| 16 |
astart += 1
|
|
| 17 |
bstart += 1
|
|
| 18 |
end
|
|
| 19 |
|
|
| 20 |
# now the end
|
|
| 21 |
while (astart <= afinish && bstart <= bfinish && a[afinish] == b[bfinish])
|
|
| 22 |
mvector[afinish] = bfinish
|
|
| 23 |
afinish -= 1
|
|
| 24 |
bfinish -= 1
|
|
| 25 |
end
|
|
| 25 | 26 | |
| 26 |
bmatches = b.reverse_hash(bstart..bfinish) |
|
| 27 |
thresh = [] |
|
| 28 |
links = [] |
|
| 29 |
|
|
| 30 |
(astart..afinish).each { |aindex|
|
|
| 31 |
aelem = a[aindex] |
|
| 32 |
next unless bmatches.has_key? aelem |
|
| 33 |
k = nil |
|
| 34 |
bmatches[aelem].reverse.each { |bindex|
|
|
| 35 |
if k && (thresh[k] > bindex) && (thresh[k-1] < bindex) |
|
| 36 |
thresh[k] = bindex |
|
| 37 |
else |
|
| 38 |
k = thresh.replacenextlarger(bindex, k) |
|
| 39 |
end |
|
| 40 |
links[k] = [ (k==0) ? nil : links[k-1], aindex, bindex ] if k |
|
| 27 |
bmatches = b.reverse_hash(bstart..bfinish) |
|
| 28 |
thresh = [] |
|
| 29 |
links = [] |
|
| 30 |
|
|
| 31 |
(astart..afinish).each { |aindex|
|
|
| 32 |
aelem = a[aindex] |
|
| 33 |
next unless bmatches.has_key? aelem |
|
| 34 |
k = nil |
|
| 35 |
bmatches[aelem].reverse.each { |bindex|
|
|
| 36 |
if k && (thresh[k] > bindex) && (thresh[k-1] < bindex) |
|
| 37 |
thresh[k] = bindex |
|
| 38 |
else |
|
| 39 |
k = thresh.replacenextlarger(bindex, k) |
|
| 40 |
end |
|
| 41 |
links[k] = [ (k==0) ? nil : links[k-1], aindex, bindex ] if k |
|
| 42 |
} |
|
| 41 | 43 |
} |
| 42 |
} |
|
| 43 | 44 | |
| 44 |
if !thresh.empty? |
|
| 45 |
link = links[thresh.length-1] |
|
| 46 |
while link |
|
| 47 |
mvector[link[1]] = link[2] |
|
| 48 |
link = link[0] |
|
| 45 |
if !thresh.empty? |
|
| 46 |
link = links[thresh.length-1] |
|
| 47 |
while link |
|
| 48 |
mvector[link[1]] = link[2] |
|
| 49 |
link = link[0] |
|
| 50 |
end |
|
| 49 | 51 |
end |
| 50 |
end |
|
| 51 | 52 | |
| 52 |
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 |
|
| 53 |
return mvector |
|
| 75 | 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 | 63 |
discardb(bi, b[bi]) |
| 78 | 64 |
bi += 1 |
| 79 | 65 |
end |
| 80 | 66 |
match(ai, bi) |
| 81 |
1 |
|
| 82 |
end |
|
| 83 | ||
| 84 |
def compactdiffs |
|
| 85 |
diffs = [] |
|
| 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] |
|
| 67 |
bi += 1 |
|
| 68 |
else |
|
| 69 |
discarda(ai, a[ai]) |
|
| 70 |
end |
|
| 71 |
ai += 1 |
|
| 101 | 72 |
end |
| 102 |
diffs.push curdiff |
|
| 103 |
} |
|
| 104 |
return diffs |
|
| 105 |
end |
|
| 73 |
while ai < a.length |
|
| 74 |
discarda(ai, a[ai]) |
|
| 75 |
ai += 1 |
|
| 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) |
|
| 110 |
if b.nil? |
|
| 111 |
@diffs = diffs_or_a |
|
| 112 |
@isstring = isstring |
|
| 113 |
else |
|
| 114 |
@diffs = [] |
|
| 108 |
attr_reader :diffs, :difftype |
|
| 109 | ||
| 110 |
def initialize(diffs_or_a, b = nil, isstring = nil) |
|
| 111 |
if b.nil? |
|
| 112 |
@diffs = diffs_or_a |
|
| 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 | 124 |
@curdiffs = [] |
| 116 |
makediff(diffs_or_a, b) |
|
| 117 |
@difftype = diffs_or_a.class |
|
| 118 | 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 |
@curdiffs.push ['-', i, elem] |
|
| 128 |
end |
|
| 127 |
def discarda(i, elem)
|
|
| 128 |
@curdiffs.push ['-', i, elem]
|
|
| 129 |
end
|
|
| 129 | 130 | |
| 130 |
def discardb(i, elem) |
|
| 131 |
@curdiffs.push ['+', i, elem] |
|
| 132 |
end |
|
| 131 |
def discardb(i, elem)
|
|
| 132 |
@curdiffs.push ['+', i, elem]
|
|
| 133 |
end
|
|
| 133 | 134 | |
| 134 |
def compact |
|
| 135 |
return Diff.new(compactdiffs) |
|
| 136 |
end |
|
| 135 |
def compact
|
|
| 136 |
return Diff.new(compactdiffs)
|
|
| 137 |
end
|
|
| 137 | 138 | |
| 138 |
def compact! |
|
| 139 |
@diffs = compactdiffs |
|
| 140 |
end |
|
| 139 |
def compact!
|
|
| 140 |
@diffs = compactdiffs
|
|
| 141 |
end
|
|
| 141 | 142 | |
| 142 |
def inspect |
|
| 143 |
@diffs.inspect |
|
| 144 |
end |
|
| 143 |
def inspect
|
|
| 144 |
@diffs.inspect
|
|
| 145 |
end
|
|
| 145 | 146 | |
| 147 |
end |
|
| 146 | 148 |
end |
| 147 | 149 | |
| 148 | 150 |
module Diffable |
| 149 | 151 |
def diff(b) |
| 150 |
Diff.new(self, b) |
|
| 152 |
RedmineDiff::Diff.new(self, b)
|
|
| 151 | 153 |
end |
| 152 | 154 | |
| 153 | 155 |
# Create a hash that maps elements of the array to arrays of indices |
| ... | ... | |
| 158 | 160 |
range.each { |i|
|
| 159 | 161 |
elem = self[i] |
| 160 | 162 |
if revmap.has_key? elem |
| 161 |
revmap[elem].push i
|
|
| 163 |
revmap[elem].push i
|
|
| 162 | 164 |
else |
| 163 |
revmap[elem] = [i]
|
|
| 165 |
revmap[elem] = [i]
|
|
| 164 | 166 |
end |
| 165 | 167 |
} |
| 166 | 168 |
return revmap |
| ... | ... | |
| 179 | 181 |
found = self[index] |
| 180 | 182 |
return nil if value == found |
| 181 | 183 |
if value > found |
| 182 |
low = index + 1
|
|
| 184 |
low = index + 1
|
|
| 183 | 185 |
else |
| 184 |
high = index
|
|
| 186 |
high = index
|
|
| 185 | 187 |
end |
| 186 | 188 |
end |
| 187 | 189 | |
| ... | ... | |
| 204 | 206 |
bi = 0 |
| 205 | 207 |
diff.diffs.each { |d|
|
| 206 | 208 |
d.each { |mod|
|
| 207 |
case mod[0]
|
|
| 208 |
when '-'
|
|
| 209 |
while ai < mod[1]
|
|
| 210 |
newary << self[ai]
|
|
| 211 |
ai += 1
|
|
| 212 |
bi += 1
|
|
| 213 |
end
|
|
| 214 |
ai += 1
|
|
| 215 |
when '+'
|
|
| 216 |
while bi < mod[1]
|
|
| 217 |
newary << self[ai]
|
|
| 218 |
ai += 1
|
|
| 219 |
bi += 1
|
|
| 220 |
end
|
|
| 221 |
newary << mod[2]
|
|
| 222 |
bi += 1
|
|
| 223 |
else
|
|
| 224 |
raise "Unknown diff action"
|
|
| 225 |
end
|
|
| 209 |
case mod[0]
|
|
| 210 |
when '-'
|
|
| 211 |
while ai < mod[1]
|
|
| 212 |
newary << self[ai]
|
|
| 213 |
ai += 1
|
|
| 214 |
bi += 1
|
|
| 215 |
end
|
|
| 216 |
ai += 1
|
|
| 217 |
when '+'
|
|
| 218 |
while bi < mod[1]
|
|
| 219 |
newary << self[ai]
|
|
| 220 |
ai += 1
|
|
| 221 |
bi += 1
|
|
| 222 |
end
|
|
| 223 |
newary << mod[2]
|
|
| 224 |
bi += 1
|
|
| 225 |
else
|
|
| 226 |
raise "Unknown diff action"
|
|
| 227 |
end
|
|
| 226 | 228 |
} |
| 227 | 229 |
} |
| 228 | 230 |
while ai < self.length |
| ... | ... | |
| 243 | 245 |
end |
| 244 | 246 | |
| 245 | 247 |
=begin |
| 246 |
= Diff |
|
| 247 |
(({diff.rb})) - computes the differences between two arrays or
|
|
| 248 |
strings. Copyright (C) 2001 Lars Christensen |
|
| 248 |
= Diff
|
|
| 249 |
(({diff.rb})) - computes the differences between two arrays or
|
|
| 250 |
strings. Copyright (C) 2001 Lars Christensen
|
|
| 249 | 251 | |
| 250 |
== Synopsis |
|
| 252 |
== Synopsis
|
|
| 251 | 253 | |
| 252 |
diff = Diff.new(a, b) |
|
| 253 |
b = a.patch(diff) |
|
| 254 |
diff = Diff.new(a, b)
|
|
| 255 |
b = a.patch(diff)
|
|
| 254 | 256 | |
| 255 |
== Class Diff |
|
| 256 |
=== Class Methods |
|
| 257 |
--- Diff.new(a, b) |
|
| 258 |
--- a.diff(b) |
|
| 259 |
Creates a Diff object which represent the differences between |
|
| 260 |
((|a|)) and ((|b|)). ((|a|)) and ((|b|)) can be either be arrays |
|
| 261 |
of any objects, strings, or object of any class that include |
|
| 262 |
module ((|Diffable|)) |
|
| 257 |
== Class Diff
|
|
| 258 |
=== Class Methods
|
|
| 259 |
--- Diff.new(a, b)
|
|
| 260 |
--- a.diff(b)
|
|
| 261 |
Creates a Diff object which represent the differences between
|
|
| 262 |
((|a|)) and ((|b|)). ((|a|)) and ((|b|)) can be either be arrays
|
|
| 263 |
of any objects, strings, or object of any class that include
|
|
| 264 |
module ((|Diffable|))
|
|
| 263 | 265 | |
| 264 |
== Module Diffable |
|
| 265 |
The module ((|Diffable|)) is intended to be included in any class for |
|
| 266 |
which differences are to be computed. Diffable is included into String |
|
| 267 |
and Array when (({diff.rb})) is (({require}))'d.
|
|
| 266 |
== Module Diffable
|
|
| 267 |
The module ((|Diffable|)) is intended to be included in any class for
|
|
| 268 |
which differences are to be computed. Diffable is included into String
|
|
| 269 |
and Array when (({diff.rb})) is (({require}))'d.
|
|
| 268 | 270 | |
| 269 |
Classes including Diffable should implement (({[]})) to get element at
|
|
| 270 |
integer indices, (({<<})) to append elements to the object and
|
|
| 271 |
(({ClassName#new})) should accept 0 arguments to create a new empty
|
|
| 272 |
object. |
|
| 271 |
Classes including Diffable should implement (({[]})) to get element at
|
|
| 272 |
integer indices, (({<<})) to append elements to the object and
|
|
| 273 |
(({ClassName#new})) should accept 0 arguments to create a new empty
|
|
| 274 |
object.
|
|
| 273 | 275 | |
| 274 |
=== Instance Methods |
|
| 275 |
--- Diffable#patch(diff) |
|
| 276 |
Applies the differences from ((|diff|)) to the object ((|obj|)) |
|
| 277 |
and return the result. ((|obj|)) is not changed. ((|obj|)) and |
|
| 278 |
can be either an array or a string, but must match the object |
|
| 279 |
from which the ((|diff|)) was created. |
|
| 276 |
=== Instance Methods
|
|
| 277 |
--- Diffable#patch(diff)
|
|
| 278 |
Applies the differences from ((|diff|)) to the object ((|obj|))
|
|
| 279 |
and return the result. ((|obj|)) is not changed. ((|obj|)) and
|
|
| 280 |
can be either an array or a string, but must match the object
|
|
| 281 |
from which the ((|diff|)) was created.
|
|
| 280 | 282 |
=end |
| lib/redmine/scm/adapters/abstract_adapter.rb | ||
|---|---|---|
| 100 | 100 |
def entries(path=nil, identifier=nil) |
| 101 | 101 |
return nil |
| 102 | 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 | 116 |
def properties(path, identifier=nil) |
| 105 | 117 |
return nil |
| ... | ... | |
| 260 | 272 |
|
| 261 | 273 |
class Revision |
| 262 | 274 |
attr_accessor :identifier, :scmid, :name, :author, :time, :message, :paths, :revision, :branch |
| 275 | ||
| 263 | 276 |
def initialize(attributes={})
|
| 264 | 277 |
self.identifier = attributes[:identifier] |
| 265 | 278 |
self.scmid = attributes[:scmid] |
| ... | ... | |
| 271 | 284 |
self.revision = attributes[:revision] |
| 272 | 285 |
self.branch = attributes[:branch] |
| 273 | 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 | 306 |
end |
| 276 | 307 |
|
| 277 | 308 |
class Annotate |
| lib/redmine/scm/adapters/git_adapter.rb | ||
|---|---|---|
| 21 | 21 |
module Scm |
| 22 | 22 |
module Adapters |
| 23 | 23 |
class GitAdapter < AbstractAdapter |
| 24 |
|
|
| 25 | 24 |
# Git executable name |
| 26 | 25 |
GIT_BIN = "git" |
| 27 | 26 | |
| 28 |
# Get the revision of a particuliar file |
|
| 29 |
def get_rev (rev,path) |
|
| 30 |
|
|
| 31 |
if rev != 'latest' && !rev.nil? |
|
| 32 |
cmd="#{GIT_BIN} --git-dir #{target('')} show --date=iso --pretty=fuller #{shell_quote rev} -- #{shell_quote path}"
|
|
| 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}"
|
|
| 27 |
def info |
|
| 28 |
begin |
|
| 29 |
Info.new(:root_url => url, :lastrev => lastrev('',nil))
|
|
| 30 |
rescue |
|
| 31 |
nil |
|
| 36 | 32 |
end |
| 37 |
rev=[] |
|
| 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 |
|
| 33 |
end |
|
| 43 | 34 | |
| 35 |
def branches |
|
| 36 |
branches = [] |
|
| 37 |
cmd = "cd #{target('')} && #{GIT_BIN} branch"
|
|
| 38 |
shellout(cmd) do |io| |
|
| 44 | 39 |
io.each_line do |line| |
| 45 |
if line =~ /^commit ([0-9a-f]{40})$/
|
|
| 46 |
key = "commit" |
|
| 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 | ||
| 40 |
branches << line.match('\s*\*?\s*(.*)$')[1]
|
|
| 41 |
end |
|
| 91 | 42 |
end |
| 92 | ||
| 93 |
get_rev('latest',path) if rev == []
|
|
| 94 | ||
| 95 |
return nil if $? && $?.exitstatus != 0 |
|
| 96 |
return rev |
|
| 43 |
branches.sort! |
|
| 97 | 44 |
end |
| 98 | 45 | |
| 99 |
def info |
|
| 100 |
revs = revisions(url,nil,nil,{:limit => 1})
|
|
| 101 |
if revs && revs.any? |
|
| 102 |
Info.new(:root_url => url, :lastrev => revs.first) |
|
| 103 |
else |
|
| 104 |
nil |
|
| 46 |
def tags |
|
| 47 |
tags = [] |
|
| 48 |
cmd = "cd #{target('')} && #{GIT_BIN} tag"
|
|
| 49 |
shellout(cmd) do |io| |
|
| 50 |
io.readlines.sort!.map{|t| t.strip}
|
|
| 105 | 51 |
end |
| 106 |
rescue Errno::ENOENT => e |
|
| 107 |
return nil |
|
| 52 |
end |
|
| 53 | ||
| 54 |
def default_branch |
|
| 55 |
branches.include?('master') ? 'master' : branches.first
|
|
| 108 | 56 |
end |
| 109 | 57 |
|
| 110 | 58 |
def entries(path=nil, identifier=nil) |
| ... | ... | |
| 121 | 69 |
sha = $2 |
| 122 | 70 |
size = $3 |
| 123 | 71 |
name = $4 |
| 72 |
full_path = path.empty? ? name : "#{path}/#{name}"
|
|
| 124 | 73 |
entries << Entry.new({:name => name,
|
| 125 |
:path => (path.empty? ? name : "#{path}/#{name}"),
|
|
| 126 |
:kind => ((type == "tree") ? 'dir' : 'file'), |
|
| 127 |
:size => ((type == "tree") ? nil : size), |
|
| 128 |
:lastrev => get_rev(identifier,(path.empty? ? name : "#{path}/#{name}"))
|
|
| 129 |
|
|
| 130 |
}) unless entries.detect{|entry| entry.name == name}
|
|
| 74 |
:path => full_path, |
|
| 75 |
:kind => (type == "tree") ? 'dir' : 'file', |
|
| 76 |
:size => (type == "tree") ? nil : size, |
|
| 77 |
:lastrev => lastrev(full_path,identifier) |
|
| 78 |
}) unless entries.detect{|entry| entry.name == name}
|
|
| 131 | 79 |
end |
| 132 | 80 |
end |
| 133 | 81 |
end |
| 134 | 82 |
return nil if $? && $?.exitstatus != 0 |
| 135 | 83 |
entries.sort_by_name |
| 136 | 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 |
id = io.gets.split[1] |
|
| 93 |
author = io.gets.match('Author:\s+(.*)$')[1]
|
|
| 94 |
2.times { io.gets }
|
|
| 95 |
time = io.gets.match('CommitDate:\s+(.*)$')[1]
|
|
| 96 | ||
| 97 |
Revision.new({
|
|
| 98 |
:identifier => id, |
|
| 99 |
:scmid => id, |
|
| 100 |
:author => author, |
|
| 101 |
:time => time, |
|
| 102 |
:message => nil, |
|
| 103 |
:paths => nil |
|
| 104 |
}) |
|
| 105 |
end |
|
| 106 |
end |
|
| 107 | ||
| 108 |
def num_revisions |
|
| 109 |
cmd = "#{GIT_BIN} --git-dir #{target('')} log --all --pretty=format:'' | wc -l"
|
|
| 110 |
shellout(cmd) {|io| io.gets.chomp.to_i + 1 }
|
|
| 111 |
end |
|
| 112 | ||
| 138 | 113 |
def revisions(path, identifier_from, identifier_to, options={})
|
| 139 | 114 |
revisions = Revisions.new |
| 140 |
cmd = "#{GIT_BIN} --git-dir #{target('')} log --raw --date=iso --pretty=fuller"
|
|
| 115 | ||
| 116 |
cmd = "#{GIT_BIN} --git-dir #{target('')} log --find-copies-harder --raw --date=iso --pretty=fuller"
|
|
| 141 | 117 |
cmd << " --reverse" if options[:reverse] |
| 142 |
cmd << " -n #{options[:limit].to_i} " if (!options.nil?) && options[:limit]
|
|
| 118 |
cmd << " --all" if options[:all] |
|
| 119 |
cmd << " -n #{options[:limit]} " if options[:limit]
|
|
| 143 | 120 |
cmd << " #{shell_quote(identifier_from + '..')} " if identifier_from
|
| 144 | 121 |
cmd << " #{shell_quote identifier_to} " if identifier_to
|
| 122 |
cmd << " -- #{path}" if path && !path.empty?
|
|
| 123 | ||
| 145 | 124 |
shellout(cmd) do |io| |
| 146 | 125 |
files=[] |
| 147 | 126 |
changeset = {}
|
| ... | ... | |
| 154 | 133 |
value = $1 |
| 155 | 134 |
if (parsing_descr == 1 || parsing_descr == 2) |
| 156 | 135 |
parsing_descr = 0 |
| 157 |
revision = Revision.new({:identifier => changeset[:commit],
|
|
| 158 |
:scmid => changeset[:commit], |
|
| 159 |
:author => changeset[:author], |
|
| 160 |
:time => Time.parse(changeset[:date]), |
|
| 161 |
:message => changeset[:description], |
|
| 162 |
:paths => files |
|