# HG changeset patch # User Chris Cannam # Date 1290174100 0 # Node ID 33d69fee1d992db9394dfd9b5ed0e9d28b65f5eb # Parent 192d132064a5bb020da68969684a2fbdf84707fa# Parent 94944d00e43cd206b713c568d58887f7fb65eca4 * Merge SVN update from default branch diff -r 94944d00e43c -r 33d69fee1d99 app/helpers/application_helper.rb --- a/app/helpers/application_helper.rb Fri Nov 19 13:24:41 2010 +0000 +++ b/app/helpers/application_helper.rb Fri Nov 19 13:41:40 2010 +0000 @@ -104,8 +104,10 @@ # * :text - Link text (default to the formatted revision) def link_to_revision(revision, project, options={}) text = options.delete(:text) || format_revision(revision) + rev = revision.respond_to?(:identifier) ? revision.identifier : revision - link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => revision}, :title => l(:label_revision_id, revision)) + link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => rev}, + :title => l(:label_revision_id, format_revision(revision))) end # Generates a link to a project if active @@ -642,7 +644,7 @@ end when 'commit' if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"])) - link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision}, + link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.identifier}, :class => 'changeset', :title => truncate_single_line(changeset.comments, :length => 100) end diff -r 94944d00e43c -r 33d69fee1d99 app/helpers/repositories_helper.rb --- a/app/helpers/repositories_helper.rb Fri Nov 19 13:24:41 2010 +0000 +++ b/app/helpers/repositories_helper.rb Fri Nov 19 13:41:40 2010 +0000 @@ -18,9 +18,24 @@ require 'iconv' module RepositoriesHelper - def format_revision(txt) - txt.to_s[0,8] + # truncate rev to 8 chars if it's quite long + def truncate_long_revision_name(rev) + rev.to_s.size <= 12 ? rev.to_s : rev.to_s[0, 8] end + private :truncate_long_revision_name + + def format_revision(revision) + if [:identifier, :revision, :scmid].all? { |e| revision.respond_to? e } + if revision.scmid and revision.revision != revision.scmid and /[^\d]/ !~ revision.revision + "#{revision.revision}:#{revision.scmid}" # number:hashid + else + truncate_long_revision_name(revision.identifier) + end + else + truncate_long_revision_name(revision) + end + end + module_function :format_revision # callable as RepositoriesHelper.format_revision def truncate_at_line_break(text, length = 255) if text @@ -87,7 +102,7 @@ :action => 'show', :id => @project, :path => path_param, - :rev => @changeset.revision) + :rev => @changeset.identifier) output << "
  • #{text}
  • " output << render_changes_tree(s) elsif c = tree[file][:c] @@ -97,13 +112,13 @@ :action => 'entry', :id => @project, :path => path_param, - :rev => @changeset.revision) unless c.action == 'D' + :rev => @changeset.identifier) unless c.action == 'D' text << " - #{c.revision}" unless c.revision.blank? text << ' (' + link_to('diff', :controller => 'repositories', :action => 'diff', :id => @project, :path => path_param, - :rev => @changeset.revision) + ') ' if c.action == 'M' + :rev => @changeset.identifier) + ') ' if c.action == 'M' text << ' ' + content_tag('span', c.from_path, :class => 'copied-from') unless c.from_path.blank? output << "
  • #{text}
  • " end @@ -166,7 +181,8 @@ end def mercurial_field_tags(form, repository) - content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?))) + content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => true)) +# (repository && !repository.root_url.blank?))) end def git_field_tags(form, repository) diff -r 94944d00e43c -r 33d69fee1d99 app/models/changeset.rb --- a/app/models/changeset.rb Fri Nov 19 13:24:41 2010 +0000 +++ b/app/models/changeset.rb Fri Nov 19 13:41:40 2010 +0000 @@ -23,10 +23,10 @@ has_many :changes, :dependent => :delete_all has_and_belongs_to_many :issues - acts_as_event :title => Proc.new {|o| "#{l(:label_revision)} #{o.revision}" + (o.short_comments.blank? ? '' : (': ' + o.short_comments))}, + acts_as_event :title => Proc.new {|o| "#{l(:label_revision)} #{RepositoriesHelper.format_revision(o)}" + (o.short_comments.blank? ? '' : (': ' + o.short_comments))}, :description => :long_comments, :datetime => :committed_on, - :url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project, :rev => o.revision}} + :url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project, :rev => o.identifier}} acts_as_searchable :columns => 'comments', :include => {:repository => :project}, @@ -47,6 +47,22 @@ def revision=(r) write_attribute :revision, (r.nil? ? nil : r.to_s) end + + # Returns the identifier of this changeset. + # e.g. revision number for centralized system; hash id for DVCS + def identifier + scmid || revision + end + + # Returns the wiki identifier, "rN" or "commit:ABCDEF" + def wiki_identifier + if scmid # hash-like + "commit:#{scmid}" + else # numeric + "r#{revision}" + end + end + private :wiki_identifier def comments=(comment) write_attribute(:comments, Changeset.normalize_comments(comment)) @@ -108,11 +124,7 @@ issue.reload # don't change the status is the issue is closed next if issue.status.is_closed? - csettext = "r#{self.revision}" - if self.scmid && (! (csettext =~ /^r[0-9]+$/)) - csettext = "commit:\"#{self.scmid}\"" - end - journal = issue.init_journal(user || User.anonymous, ll(Setting.default_language, :text_status_changed_by_changeset, csettext)) + journal = issue.init_journal(user || User.anonymous, ll(Setting.default_language, :text_status_changed_by_changeset, wiki_identifier)) issue.status = fix_status unless Setting.commit_fix_done_ratio.blank? issue.done_ratio = Setting.commit_fix_done_ratio.to_i diff -r 94944d00e43c -r 33d69fee1d99 app/models/issue.rb --- a/app/models/issue.rb Fri Nov 19 13:24:41 2010 +0000 +++ b/app/models/issue.rb Fri Nov 19 13:41:40 2010 +0000 @@ -27,7 +27,7 @@ has_many :journals, :as => :journalized, :dependent => :destroy has_many :time_entries, :dependent => :delete_all - has_and_belongs_to_many :changesets, :order => "#{Changeset.table_name}.committed_on ASC, #{Changeset.table_name}.id ASC" + has_and_belongs_to_many :changesets, :order => "#{Changeset.table_name}.id ASC" has_many :relations_from, :class_name => 'IssueRelation', :foreign_key => 'issue_from_id', :dependent => :delete_all has_many :relations_to, :class_name => 'IssueRelation', :foreign_key => 'issue_to_id', :dependent => :delete_all diff -r 94944d00e43c -r 33d69fee1d99 app/models/repository.rb --- a/app/models/repository.rb Fri Nov 19 13:24:41 2010 +0000 +++ b/app/models/repository.rb Fri Nov 19 13:41:40 2010 +0000 @@ -17,7 +17,7 @@ class Repository < ActiveRecord::Base belongs_to :project - has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC" + has_many :changesets, :order => "#{Changeset.table_name}.id DESC" has_many :changes, :through => :changesets # Raw SQL to delete changesets and changes in the database @@ -94,7 +94,10 @@ # Finds and returns a revision with a number or the beginning of a hash def find_changeset_by_name(name) - changesets.find(:first, :conditions => (name.match(/^\d*$/) ? ["revision = ?", name.to_s] : ["revision LIKE ?", name + '%'])) + # TODO: is this query efficient enough? can we write as single query? + e = changesets.find(:first, :conditions => ['revision = ? OR scmid = ?', name.to_s, name.to_s]) + return e if e + changesets.find(:first, :conditions => ['scmid LIKE ?', "#{name}%"]) end def latest_changeset @@ -106,12 +109,11 @@ def latest_changesets(path, rev, limit=10) if path.blank? changesets.find(:all, :include => :user, - :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC", :limit => limit) else changes.find(:all, :include => {:changeset => :user}, :conditions => ["path = ?", path.with_leading_slash], - :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC", + :order => "#{Changeset.table_name}.id DESC", :limit => limit).collect(&:changeset) end end @@ -172,7 +174,11 @@ def self.fetch_changesets Project.active.has_module(:repository).find(:all, :include => :repository).each do |project| if project.repository - project.repository.fetch_changesets + begin + project.repository.fetch_changesets + rescue Redmine::Scm::Adapters::CommandFailed => e + logger.error "Repository: error during fetching changesets: #{e.message}" + end end end end diff -r 94944d00e43c -r 33d69fee1d99 app/models/repository/git.rb --- a/app/models/repository/git.rb Fri Nov 19 13:24:41 2010 +0000 +++ b/app/models/repository/git.rb Fri Nov 19 13:41:40 2010 +0000 @@ -49,7 +49,7 @@ c = changesets.find(:first, :order => 'committed_on DESC') since = (c ? c.committed_on - 7.days : nil) - revisions = scm.revisions('', nil, nil, :all => true, :since => since) + revisions = scm.revisions('', nil, nil, :all => true, :since => since, :reverse => true) return if revisions.nil? || revisions.empty? recent_changesets = changesets.find(:all, :conditions => ['committed_on >= ?', since]) @@ -75,7 +75,7 @@ "scmid IN (?)", revisions.map!{|c| c.scmid} ], - :order => 'committed_on DESC' + :order => 'id DESC' ) end end diff -r 94944d00e43c -r 33d69fee1d99 app/models/repository/mercurial.rb --- a/app/models/repository/mercurial.rb Fri Nov 19 13:24:41 2010 +0000 +++ b/app/models/repository/mercurial.rb Fri Nov 19 13:41:40 2010 +0000 @@ -21,6 +21,8 @@ attr_protected :root_url validates_presence_of :url + FETCH_AT_ONCE = 100 # number of changesets to fetch at once + def scm_adapter Redmine::Scm::Adapters::MercurialAdapter end @@ -30,61 +32,64 @@ end def entries(path=nil, identifier=nil) - entries=scm.entries(path, identifier) - if entries - entries.each do |entry| - next unless entry.is_file? - # Set the filesize unless browsing a specific revision - if identifier.nil? - full_path = File.join(root_url, entry.path) - entry.size = File.stat(full_path).size if File.file?(full_path) - end - # Search the DB for the entry's last change - change = changes.find(:first, :conditions => ["path = ?", scm.with_leading_slash(entry.path)], :order => "#{Changeset.table_name}.committed_on DESC") - if change - entry.lastrev.identifier = change.changeset.revision - entry.lastrev.name = change.changeset.revision - entry.lastrev.author = change.changeset.committer - entry.lastrev.revision = change.revision + scm.entries(path, identifier) + end + + def branches + bras = scm.branches + bras.sort unless bras == %w|default| + end + + # Returns the latest changesets for +path+ + def latest_changesets(path, rev, limit=10) + changesets.find(:all, :include => :user, + :conditions => latest_changesets_cond(path, rev, limit), + :limit => limit) + end + + def latest_changesets_cond(path, rev, limit) + cond, args = [], [] + + if scm.branchmap.member? rev + # dirty hack to filter by branch. branch name should be in database. + cond << "#{Changeset.table_name}.scmid IN (?)" + args << scm.nodes_in_branch(rev, path, rev, 0, :limit => limit) + elsif last = rev ? find_changeset_by_name(scm.tagmap[rev] || rev) : nil + cond << "#{Changeset.table_name}.id <= ?" + args << last.id + end + + unless path.blank? + # TODO: there must be a better way to build sub-query + cond << "EXISTS (SELECT * FROM #{Change.table_name} + WHERE #{Change.table_name}.changeset_id = #{Changeset.table_name}.id + AND (#{Change.table_name}.path = ? OR #{Change.table_name}.path LIKE ?))" + args << path.with_leading_slash << "#{path.with_leading_slash}/%" + end + + [cond.join(' AND '), *args] unless cond.empty? + end + private :latest_changesets_cond + + def fetch_changesets + scm_rev = scm.info.lastrev.revision.to_i + db_rev = latest_changeset ? latest_changeset.revision.to_i : -1 + return unless db_rev < scm_rev # already up-to-date + + logger.debug "Fetching changesets for repository #{url}" if logger + (db_rev + 1).step(scm_rev, FETCH_AT_ONCE) do |i| + transaction do + scm.each_revision('', i, [i + FETCH_AT_ONCE - 1, scm_rev].min) do |re| + cs = Changeset.create(:repository => self, + :revision => re.revision, + :scmid => re.scmid, + :committer => re.author, + :committed_on => re.time, + :comments => re.message) + re.paths.each { |e| cs.create_change(e) } end end end - entries - end - - def fetch_changesets - scm_info = scm.info - if scm_info - # latest revision found in database - db_revision = latest_changeset ? latest_changeset.revision.to_i : -1 - # latest revision in the repository - latest_revision = scm_info.lastrev - return if latest_revision.nil? - scm_revision = latest_revision.identifier.to_i - if db_revision < scm_revision - logger.debug "Fetching changesets for repository #{url}" if logger && logger.debug? - identifier_from = db_revision + 1 - while (identifier_from <= scm_revision) - # loads changesets by batches of 100 - identifier_to = [identifier_from + 99, scm_revision].min - revisions = scm.revisions('', identifier_from, identifier_to, :with_paths => true) - transaction do - revisions.each do |revision| - changeset = Changeset.create(:repository => self, - :revision => revision.identifier, - :scmid => revision.scmid, - :committer => revision.author, - :committed_on => revision.time, - :comments => revision.message) - - revision.paths.each do |change| - changeset.create_change(change) - end - end - end unless revisions.nil? - identifier_from = identifier_to + 1 - end - end - end + self end end diff -r 94944d00e43c -r 33d69fee1d99 app/views/layouts/base.rhtml --- a/app/views/layouts/base.rhtml Fri Nov 19 13:24:41 2010 +0000 +++ b/app/views/layouts/base.rhtml Fri Nov 19 13:41:40 2010 +0000 @@ -31,7 +31,7 @@ <%= render_menu :top_menu -%> -