From fcb281e5a47d867423f66f657dbe3b4b74675bbe Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Mon, 22 Mar 2010 16:30:33 +0900 Subject: [PATCH 01/60] mercurial: get client_version correctly even if LANG is set * Assumes the version number is of the first line of the output. * Uses shellout * Uses class' instance variables instead of wide-scope class variables, because it's only referenced by class method. * Changed not to cache template_path because it isn't a heavy operation. closes #5117 Test-case-by: Toshi MARUYAMA --- lib/redmine/scm/adapters/mercurial_adapter.rb | 12 ++--- .../scm/adapters/mercurial_adapter_test.rb | 47 +++++++++++++------ 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/lib/redmine/scm/adapters/mercurial_adapter.rb b/lib/redmine/scm/adapters/mercurial_adapter.rb index 9d5e9aa1164..3f0a35b41fa 100644 --- a/lib/redmine/scm/adapters/mercurial_adapter.rb +++ b/lib/redmine/scm/adapters/mercurial_adapter.rb @@ -30,25 +30,23 @@ class MercurialAdapter < AbstractAdapter class << self def client_version - @@client_version ||= (hgversion || []) + @client_version ||= hgversion end def hgversion # The hg version is expressed either as a # release number (eg 0.9.5 or 1.0) or as a revision # id composed of 12 hexa characters. - theversion = hgversion_from_command_line - if theversion.match(/^\d+(\.\d+)+/) - theversion.split(".").collect(&:to_i) - end + hgversion_from_command_line[/\d+(\.\d+)+/].to_s.split('.').map { |e| e.to_i } end def hgversion_from_command_line - %x{#{HG_BIN} --version}.match(/\(version (.*)\)/)[1] + shellout("#{HG_BIN} --version") { |io| io.gets }.to_s end + private :hgversion_from_command_line def template_path - @@template_path ||= template_path_for(client_version) + template_path_for(client_version) end def template_path_for(version) diff --git a/test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb b/test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb index 7396a395728..d7248224ae4 100644 --- a/test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb +++ b/test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb @@ -1,3 +1,4 @@ + require File.dirname(__FILE__) + '/../../../../../test_helper' begin require 'mocha' @@ -9,13 +10,26 @@ class MercurialAdapterTest < ActiveSupport::TestCase TEMPLATE_EXTENSION = Redmine::Scm::Adapters::MercurialAdapter::TEMPLATE_EXTENSION REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/mercurial_repository' + + def setup + @adapter = Redmine::Scm::Adapters::MercurialAdapter + end def test_hgversion - to_test = { "0.9.5" => [0,9,5], - "1.0" => [1,0], - "1e4ddc9ac9f7+20080325" => nil, - "1.0.1+20080525" => [1,0,1], - "1916e629a29d" => nil} + to_test = { + "0.9.5" => [0,9,5], + "1.0" => [1,0], + "1e4ddc9ac9f7+20080325" => [], + "1.0.1+20080525" => [1,0,1], + "1916e629a29d" => [] , + "1.6" => [1,6], + "1.6.1" => [1,6,1], + "Mercurial Distributed SCM (version 1.6.3)" => [1,6,3], + ## Italian + # $ make local + # $ LANG=it ./hg --version + "Mercurial SCM Distribuito (versione 1.6.3+61-1c9bb7e00f71)" => [1,6,3], + } to_test.each do |s, v| test_hgversion_for(s, v) @@ -23,10 +37,14 @@ def test_hgversion end def test_template_path - to_test = { [0,9,5] => "0.9.5", - [1,0] => "1.0", - [] => "1.0", - [1,0,1] => "1.0"} + to_test = { + [0,9,5] => "0.9.5", + [1,0] => "1.0" , + [] => "1.0" , + [1,0,1] => "1.0" , + [1,6] => "1.0" , + [1,6,1] => "1.0" , + } to_test.each do |v, template| test_template_path_for(v, template) @@ -36,15 +54,14 @@ def test_template_path private def test_hgversion_for(hgversion, version) - Redmine::Scm::Adapters::MercurialAdapter.expects(:hgversion_from_command_line).returns(hgversion) - adapter = Redmine::Scm::Adapters::MercurialAdapter - assert_equal version, adapter.hgversion + @adapter.expects(:hgversion_from_command_line).returns(hgversion) + assert_equal version, @adapter.hgversion end def test_template_path_for(version, template) - adapter = Redmine::Scm::Adapters::MercurialAdapter - assert_equal "#{TEMPLATES_DIR}/#{TEMPLATE_NAME}-#{template}.#{TEMPLATE_EXTENSION}", adapter.template_path_for(version) - assert File.exist?(adapter.template_path_for(version)) + assert_equal "#{TEMPLATES_DIR}/#{TEMPLATE_NAME}-#{template}.#{TEMPLATE_EXTENSION}", + @adapter.template_path_for(version) + assert File.exist?(@adapter.template_path_for(version)) end end From 97068e5e343d45597a6531d8bdcc88691249ed97 Mon Sep 17 00:00:00 2001 From: Toshi MARUYAMA Date: Sun, 10 Oct 2010 20:19:36 +0900 Subject: [PATCH 02/60] scm: mercurial: preserve seconds of committed timestamp It changes template keyword 'isodate' to 'isodatesec'. 'isodatesec' is supported since Mercurial 8999d1249171, which means Mercurial 1.0 has it. Tested-by: Yuya Nishihara Tested-on: Redmine trunk r3840 --- lib/redmine/scm/adapters/mercurial/hg-template-1.0.tmpl | 2 +- test/unit/repository_mercurial_test.rb | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/redmine/scm/adapters/mercurial/hg-template-1.0.tmpl b/lib/redmine/scm/adapters/mercurial/hg-template-1.0.tmpl index 3eef850168e..b6cc7a78692 100644 --- a/lib/redmine/scm/adapters/mercurial/hg-template-1.0.tmpl +++ b/lib/redmine/scm/adapters/mercurial/hg-template-1.0.tmpl @@ -1,7 +1,7 @@ changeset = 'This template must be used with --debug option\n' changeset_quiet = 'This template must be used with --debug option\n' changeset_verbose = 'This template must be used with --debug option\n' -changeset_debug = '\n{author|escape}\n{date|isodate}\n\n{file_mods}{file_adds}{file_dels}{file_copies}\n{desc|escape}\n{tags}\n\n' +changeset_debug = '\n{author|escape}\n{date|isodatesec}\n\n{file_mods}{file_adds}{file_dels}{file_copies}\n{desc|escape}\n{tags}\n\n' file_mod = '{file_mod|escape}\n' file_add = '{file_add|escape}\n' diff --git a/test/unit/repository_mercurial_test.rb b/test/unit/repository_mercurial_test.rb index 6ce3d5fa023..3770450ba82 100644 --- a/test/unit/repository_mercurial_test.rb +++ b/test/unit/repository_mercurial_test.rb @@ -68,6 +68,13 @@ def test_cat assert_nil @repository.scm.cat("sources/welcome_controller.rb") end + def test_isodatesec + @repository.fetch_changesets + @repository.reload + rev0_committed_on = Time.gm(2007, 12, 14, 9, 22, 52) + assert_equal @repository.changesets.find_by_revision('0').committed_on, rev0_committed_on + end + else puts "Mercurial test repository NOT FOUND. Skipping unit tests !!!" def test_fake; assert true end From 9cd232275b291f667db669c1f5be362dd24cb8e6 Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Fri, 30 Jul 2010 01:41:52 +0900 Subject: [PATCH 03/60] repository: mercurial: sort changesets by revision (fixes #3449, #3567) In DVCS, changesets are not in date order. Because Mercurial backend inserts changesets from eariest, 'ORDER BY id' means 'order by revision number'. refs #4455 Signed-off-by: Yuya Nishihara Tested-on: Redmine trunk r3840 --- app/models/repository/mercurial.rb | 15 +++++++++++++++ .../repositories/mercurial_repository.tar.gz | Bin 7827 -> 8058 bytes test/unit/repository_mercurial_test.rb | 10 ++++++++++ 3 files changed, 25 insertions(+) diff --git a/app/models/repository/mercurial.rb b/app/models/repository/mercurial.rb index 1072239567b..9ce3b63ebd8 100644 --- a/app/models/repository/mercurial.rb +++ b/app/models/repository/mercurial.rb @@ -18,6 +18,9 @@ require 'redmine/scm/adapters/mercurial_adapter' class Repository::Mercurial < Repository + # sort changesets by revision number + has_many :changesets, :order => "#{Changeset.table_name}.id DESC", :foreign_key => 'repository_id' + attr_protected :root_url validates_presence_of :url @@ -52,6 +55,18 @@ def entries(path=nil, identifier=nil) entries end + # Returns the latest changesets for +path+; sorted by revision number + def latest_changesets(path, rev, limit=10) + if path.blank? + changesets.find(:all, :include => :user, :limit => limit) + else + changes.find(:all, :include => {:changeset => :user}, + :conditions => ["path = ?", path.with_leading_slash], + :order => "#{Changeset.table_name}.id DESC", + :limit => limit).collect(&:changeset) + end + end + def fetch_changesets scm_info = scm.info if scm_info diff --git a/test/fixtures/repositories/mercurial_repository.tar.gz b/test/fixtures/repositories/mercurial_repository.tar.gz index 1d8ad305732f7a0d7bfbcd52bbeec919da88e3f1..c2a1b58745c8fdf994e7ab43b11919e79a639be4 100644 GIT binary patch literal 8058 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 I@&KR!02pZfuK)l5 literal 7827 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 diff --git a/test/unit/repository_mercurial_test.rb b/test/unit/repository_mercurial_test.rb index 3770450ba82..2a6c6a5e88c 100644 --- a/test/unit/repository_mercurial_test.rb +++ b/test/unit/repository_mercurial_test.rb @@ -75,6 +75,16 @@ def test_isodatesec assert_equal @repository.changesets.find_by_revision('0').committed_on, rev0_committed_on end + def test_changeset_order_by_revision + @repository.fetch_changesets + @repository.reload + c0 = @repository.latest_changeset + c1 = @repository.changesets(nil, nil)[1] + # sorted by revision (id), not by date + assert c0.revision.to_i > c1.revision.to_i + assert c0.committed_on < c1.committed_on + end + else puts "Mercurial test repository NOT FOUND. Skipping unit tests !!!" def test_fake; assert true end From 26ea3b391a2449d1f0cf2a68ff09d95ebe977d3f Mon Sep 17 00:00:00 2001 From: Toshi MARUYAMA Date: Sun, 10 Oct 2010 20:10:59 +0900 Subject: [PATCH 04/60] repository: mercurial: rearrange unit tests. --- .../scm/adapters/mercurial_adapter_test.rb | 39 +++++++++++++------ test/unit/repository_mercurial_test.rb | 6 --- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb b/test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb index d7248224ae4..9d6b754e58e 100644 --- a/test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb +++ b/test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb @@ -3,16 +3,14 @@ begin require 'mocha' - class MercurialAdapterTest < ActiveSupport::TestCase + class MercurialAdapterClassTest < ActiveSupport::TestCase TEMPLATES_DIR = Redmine::Scm::Adapters::MercurialAdapter::TEMPLATES_DIR TEMPLATE_NAME = Redmine::Scm::Adapters::MercurialAdapter::TEMPLATE_NAME TEMPLATE_EXTENSION = Redmine::Scm::Adapters::MercurialAdapter::TEMPLATE_EXTENSION - REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/mercurial_repository' - def setup - @adapter = Redmine::Scm::Adapters::MercurialAdapter + @adapter_class = Redmine::Scm::Adapters::MercurialAdapter end def test_hgversion @@ -50,21 +48,40 @@ def test_template_path test_template_path_for(v, template) end end - + private - + def test_hgversion_for(hgversion, version) - @adapter.expects(:hgversion_from_command_line).returns(hgversion) - assert_equal version, @adapter.hgversion + @adapter_class.expects(:hgversion_from_command_line).returns(hgversion) + assert_equal version, @adapter_class.hgversion end - + def test_template_path_for(version, template) assert_equal "#{TEMPLATES_DIR}/#{TEMPLATE_NAME}-#{template}.#{TEMPLATE_EXTENSION}", - @adapter.template_path_for(version) - assert File.exist?(@adapter.template_path_for(version)) + @adapter_class.template_path_for(version) + assert File.exist?(@adapter_class.template_path_for(version)) end end rescue LoadError def test_fake; assert(false, "Requires mocha to run those tests") end end + +class MercurialAdapterTest < ActiveSupport::TestCase + REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/mercurial_repository' + + if File.directory?(REPOSITORY_PATH) + def setup + @adapter = Redmine::Scm::Adapters::MercurialAdapter.new(REPOSITORY_PATH) + end + + def test_cat + assert @adapter.cat("sources/welcome_controller.rb", 2) + assert_nil @adapter.cat("sources/welcome_controller.rb") + end + + else + puts "Mercurial test repository NOT FOUND. Skipping unit tests !!!" + def test_fake; assert true end + end +end diff --git a/test/unit/repository_mercurial_test.rb b/test/unit/repository_mercurial_test.rb index 2a6c6a5e88c..3409fa3a802 100644 --- a/test/unit/repository_mercurial_test.rb +++ b/test/unit/repository_mercurial_test.rb @@ -62,12 +62,6 @@ def test_locate_on_outdated_repository assert_equal 2, @repository.entries("images", 2).size end - - def test_cat - assert @repository.scm.cat("sources/welcome_controller.rb", 2) - assert_nil @repository.scm.cat("sources/welcome_controller.rb") - end - def test_isodatesec @repository.fetch_changesets @repository.reload From 64432bb1ed401fe3c909ed7b23d9ba98f30a271d Mon Sep 17 00:00:00 2001 From: Toshi MARUYAMA Date: Mon, 18 Oct 2010 20:33:23 +0900 Subject: [PATCH 05/60] repository: mercurial: accept both of revision and nodeid as changeset id Signed-off-by: Yuya Nishihara Test-written-by: Toshi MARUYAMA Test-written-by: Yuya Nishihara --- app/models/repository/mercurial.rb | 11 +++++++++++ .../repositories_mercurial_controller_test.rb | 8 ++++++++ .../redmine/scm/adapters/mercurial_adapter_test.rb | 12 ++++++++++++ test/unit/repository_mercurial_test.rb | 2 ++ 4 files changed, 33 insertions(+) diff --git a/app/models/repository/mercurial.rb b/app/models/repository/mercurial.rb index 9ce3b63ebd8..8e2ed4517e5 100644 --- a/app/models/repository/mercurial.rb +++ b/app/models/repository/mercurial.rb @@ -55,6 +55,17 @@ def entries(path=nil, identifier=nil) entries end + # Finds and returns a revision with a number or the beginning of a hash + def find_changeset_by_name(name) + if /[^\d]/ =~ name or name.to_s.size > 8 + e = changesets.find(:first, :conditions => ['scmid = ?', name.to_s]) + else + e = changesets.find(:first, :conditions => ['revision = ?', name.to_s]) + end + return e if e + changesets.find(:first, :conditions => ['scmid LIKE ?', "#{name}%"]) # last ditch + end + # Returns the latest changesets for +path+; sorted by revision number def latest_changesets(path, rev, limit=10) if path.blank? diff --git a/test/functional/repositories_mercurial_controller_test.rb b/test/functional/repositories_mercurial_controller_test.rb index f2639ee9271..59ece70ea5b 100644 --- a/test/functional/repositories_mercurial_controller_test.rb +++ b/test/functional/repositories_mercurial_controller_test.rb @@ -75,6 +75,14 @@ def test_show_at_given_revision assert_equal ['delete.png'], assigns(:entries).collect(&:name) end + def test_show_at_given_nodeid + get :show, :id => 3, :path => ['images'], :rev => '0885933ad4f6' + assert_response :success + assert_template 'show' + assert_not_nil assigns(:entries) + assert_equal ['delete.png'], assigns(:entries).collect(&:name) + end + def test_changes get :changes, :id => 3, :path => ['images', 'edit.png'] assert_response :success diff --git a/test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb b/test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb index 9d6b754e58e..c1c1e062996 100644 --- a/test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb +++ b/test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb @@ -80,6 +80,18 @@ def test_cat assert_nil @adapter.cat("sources/welcome_controller.rb") end + def test_access_by_nodeid + path = 'sources/welcome_controller.rb' + assert_equal @adapter.cat(path, 2), + @adapter.cat(path, '400bb8672109') + end + + def test_access_by_fuzzy_nodeid + path = 'sources/welcome_controller.rb' + # falls back to nodeid + assert_equal @adapter.cat(path, 2), @adapter.cat(path, '400') + end + else puts "Mercurial test repository NOT FOUND. Skipping unit tests !!!" def test_fake; assert true end diff --git a/test/unit/repository_mercurial_test.rb b/test/unit/repository_mercurial_test.rb index 3409fa3a802..1773631e943 100644 --- a/test/unit/repository_mercurial_test.rb +++ b/test/unit/repository_mercurial_test.rb @@ -51,7 +51,9 @@ def test_fetch_changesets_incremental def test_entries assert_equal 2, @repository.entries("sources", 2).size + assert_equal 2, @repository.entries("sources", '400bb8672109').size assert_equal 1, @repository.entries("sources", 3).size + assert_equal 1, @repository.entries("sources", 'b3a615152df8').size end def test_locate_on_outdated_repository From 4e7aeac07d0ce379d00db7cb83f68a641e01e73f Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Tue, 23 Mar 2010 23:14:08 +0900 Subject: [PATCH 06/60] changeset, revision: add attrs to identify the changeset (refs #3724) It returns revision by default. It can be overriden by repository backends, e.g. Mercurial uses nodeid for identifier. Signed-off-by: Yuya Nishihara --- app/models/changeset.rb | 9 +++++++++ app/models/repository/mercurial.rb | 5 +++++ lib/redmine/scm/adapters/abstract_adapter.rb | 8 +++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/app/models/changeset.rb b/app/models/changeset.rb index 063a4a48ced..246184c6e05 100644 --- a/app/models/changeset.rb +++ b/app/models/changeset.rb @@ -47,6 +47,15 @@ class Changeset < ActiveRecord::Base def revision=(r) write_attribute :revision, (r.nil? ? nil : r.to_s) end + + # Returns the identifier of this changeset; depending on repository backends + def identifier + if repository.class.respond_to? :changeset_identifier + repository.class.changeset_identifier self + else + revision.to_s + end + end def comments=(comment) write_attribute(:comments, Changeset.normalize_comments(comment)) diff --git a/app/models/repository/mercurial.rb b/app/models/repository/mercurial.rb index 8e2ed4517e5..2470adbb29a 100644 --- a/app/models/repository/mercurial.rb +++ b/app/models/repository/mercurial.rb @@ -31,6 +31,11 @@ def scm_adapter def self.scm_name 'Mercurial' end + + # Returns the identifier for the given mercurial changeset + def self.changeset_identifier(changeset) + changeset.scmid + end def entries(path=nil, identifier=nil) entries=scm.entries(path, identifier) diff --git a/lib/redmine/scm/adapters/abstract_adapter.rb b/lib/redmine/scm/adapters/abstract_adapter.rb index a3ca61e2361..70597888490 100644 --- a/lib/redmine/scm/adapters/abstract_adapter.rb +++ b/lib/redmine/scm/adapters/abstract_adapter.rb @@ -271,7 +271,8 @@ def latest end class Revision - attr_accessor :identifier, :scmid, :name, :author, :time, :message, :paths, :revision, :branch + attr_accessor :scmid, :name, :author, :time, :message, :paths, :revision, :branch + attr_writer :identifier def initialize(attributes={}) self.identifier = attributes[:identifier] @@ -285,6 +286,11 @@ def initialize(attributes={}) self.branch = attributes[:branch] end + # Returns the identifier of this revision; see also Changeset model + def identifier + (@identifier || revision).to_s + end + def save(repo) Changeset.transaction do changeset = Changeset.new( From 0b04f15f31ca1bd3d350e42fc9c46510d1f905c4 Mon Sep 17 00:00:00 2001 From: Toshi MARUYAMA Date: Mon, 18 Oct 2010 20:33:23 +0900 Subject: [PATCH 07/60] changeset, revision: add format_identifier for human-readable revision id * use first 8 chars of scmid or revision by default * "rev:scmid" for Mercurial --- app/models/changeset.rb | 9 +++++++++ app/models/repository/mercurial.rb | 5 +++++ lib/redmine/scm/adapters/abstract_adapter.rb | 5 +++++ lib/redmine/scm/adapters/mercurial_adapter.rb | 7 +++++++ 4 files changed, 26 insertions(+) diff --git a/app/models/changeset.rb b/app/models/changeset.rb index 246184c6e05..a9cc49530f4 100644 --- a/app/models/changeset.rb +++ b/app/models/changeset.rb @@ -56,6 +56,15 @@ def identifier revision.to_s end end + + # Returns the readable identifier; first 8 chars of identifier by default + def format_identifier + if repository.class.respond_to? :format_changeset_identifier + repository.class.format_changeset_identifier self + else + identifier[0, 8] + end + end def comments=(comment) write_attribute(:comments, Changeset.normalize_comments(comment)) diff --git a/app/models/repository/mercurial.rb b/app/models/repository/mercurial.rb index 2470adbb29a..23196eb831f 100644 --- a/app/models/repository/mercurial.rb +++ b/app/models/repository/mercurial.rb @@ -36,6 +36,11 @@ def self.scm_name def self.changeset_identifier(changeset) changeset.scmid end + + # Returns the readable identifier for the given mercurial changeset + def self.format_changeset_identifier(changeset) + "#{changeset.revision}:#{changeset.scmid}" + end def entries(path=nil, identifier=nil) entries=scm.entries(path, identifier) diff --git a/lib/redmine/scm/adapters/abstract_adapter.rb b/lib/redmine/scm/adapters/abstract_adapter.rb index 70597888490..1cc99c86638 100644 --- a/lib/redmine/scm/adapters/abstract_adapter.rb +++ b/lib/redmine/scm/adapters/abstract_adapter.rb @@ -291,6 +291,11 @@ def identifier (@identifier || revision).to_s end + # Returns the readable identifier; first 8 chars of identifier. + def format_identifier + identifier[0, 8] + end + def save(repo) Changeset.transaction do changeset = Changeset.new( diff --git a/lib/redmine/scm/adapters/mercurial_adapter.rb b/lib/redmine/scm/adapters/mercurial_adapter.rb index 3f0a35b41fa..16d28ce1991 100644 --- a/lib/redmine/scm/adapters/mercurial_adapter.rb +++ b/lib/redmine/scm/adapters/mercurial_adapter.rb @@ -197,6 +197,13 @@ def annotate(path, identifier=nil) return nil if $? && $?.exitstatus != 0 blame end + + class Revision < Redmine::Scm::Adapters::Revision + # Returns the readable identifier + def format_identifier + "#{revision}:#{scmid}" + end + end end end end From d2e7c4bcd5f8c6e4e7200309ee8a9e90792c976e Mon Sep 17 00:00:00 2001 From: Toshi MARUYAMA Date: Mon, 18 Oct 2010 20:33:23 +0900 Subject: [PATCH 08/60] helpers: accecpt Changeset or Revision obj --- app/helpers/application_helper.rb | 4 +++- app/helpers/repositories_helper.rb | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index e2255da3094..67d83754c9c 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -104,8 +104,10 @@ def link_to_attachment(attachment, options={}) # * :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 def link_to_project(project, options={}) diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb index 95962873541..b4777ed9db5 100644 --- a/app/helpers/repositories_helper.rb +++ b/app/helpers/repositories_helper.rb @@ -18,8 +18,12 @@ require 'iconv' module RepositoriesHelper - def format_revision(txt) - txt.to_s[0,8] + def format_revision(revision) + if revision.respond_to? :format_identifier + revision.format_identifier + else + revision.to_s[0,8] + end end def truncate_at_line_break(text, length = 255) From f248cfa6ab4a85b65c545639a94d8c1d175bd5aa Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Tue, 23 Mar 2010 23:14:08 +0900 Subject: [PATCH 09/60] repository: pass object to format_revision and link_to_revision, use changeset.identifier TODO: not completed yet refs #3724 --- app/helpers/repositories_helper.rb | 6 +++--- app/views/repositories/_dir_list_content.rhtml | 2 +- app/views/repositories/_revisions.rhtml | 6 +++--- app/views/repositories/annotate.rhtml | 2 +- app/views/repositories/revision.rhtml | 8 ++++---- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb index b4777ed9db5..0bfbfabcaa5 100644 --- a/app/helpers/repositories_helper.rb +++ b/app/helpers/repositories_helper.rb @@ -91,7 +91,7 @@ def render_changes_tree(tree) :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] @@ -101,13 +101,13 @@ def render_changes_tree(tree) :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 diff --git a/app/views/repositories/_dir_list_content.rhtml b/app/views/repositories/_dir_list_content.rhtml index c5bd53ea78b..66574f1c86e 100644 --- a/app/views/repositories/_dir_list_content.rhtml +++ b/app/views/repositories/_dir_list_content.rhtml @@ -17,7 +17,7 @@ <%= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %> <% changeset = @project.repository.changesets.find_by_revision(entry.lastrev.identifier) if entry.lastrev && entry.lastrev.identifier %> -<%= link_to_revision(changeset.revision, @project) if changeset %> +<%= link_to_revision(changeset, @project) if changeset %> <%= distance_of_time_in_words(entry.lastrev.time, Time.now) if entry.lastrev && entry.lastrev.time %> <%= changeset.nil? ? h(entry.lastrev.author.to_s.split('<').first) : changeset.author if entry.lastrev %> <%=h truncate(changeset.comments, :length => 50) unless changeset.nil? %> diff --git a/app/views/repositories/_revisions.rhtml b/app/views/repositories/_revisions.rhtml index 26fb5b6992a..92c6fb535a7 100644 --- a/app/views/repositories/_revisions.rhtml +++ b/app/views/repositories/_revisions.rhtml @@ -13,9 +13,9 @@ <% line_num = 1 %> <% revisions.each do |changeset| %> -<%= link_to_revision(changeset.revision, project) %> -<%= radio_button_tag('rev', changeset.revision, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('cbto-#{line_num+1}').checked=true;") if show_diff && (line_num < revisions.size) %> -<%= radio_button_tag('rev_to', changeset.revision, (line_num==2), :id => "cbto-#{line_num}", :onclick => "if ($('cb-#{line_num}').checked==true) {$('cb-#{line_num-1}').checked=true;}") if show_diff && (line_num > 1) %> +<%= link_to_revision(changeset, project) %> +<%= radio_button_tag('rev', changeset.identifier, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('cbto-#{line_num+1}').checked=true;") if show_diff && (line_num < revisions.size) %> +<%= radio_button_tag('rev_to', changeset.identifier, (line_num==2), :id => "cbto-#{line_num}", :onclick => "if ($('cb-#{line_num}').checked==true) {$('cb-#{line_num-1}').checked=true;}") if show_diff && (line_num > 1) %> <%= format_time(changeset.committed_on) %> <%=h changeset.author %> <%= textilizable(truncate_at_line_break(changeset.comments)) %> diff --git a/app/views/repositories/annotate.rhtml b/app/views/repositories/annotate.rhtml index a18e9bbac3e..498507148de 100644 --- a/app/views/repositories/annotate.rhtml +++ b/app/views/repositories/annotate.rhtml @@ -19,7 +19,7 @@ <%= line_num %> - <%= (revision.identifier ? link_to(format_revision(revision.identifier), :action => 'revision', :id => @project, :rev => revision.identifier) : format_revision(revision.revision)) if revision %> + <%= (revision.identifier ? link_to_revision(revision, @project) : format_revision(revision)) if revision %> <%= h(revision.author.to_s.split('<').first) if revision %>
    <%= line %>
    diff --git a/app/views/repositories/revision.rhtml b/app/views/repositories/revision.rhtml index 92597dff7c9..fc942f4c246 100644 --- a/app/views/repositories/revision.rhtml +++ b/app/views/repositories/revision.rhtml @@ -1,13 +1,13 @@
    « <% unless @changeset.previous.nil? -%> - <%= link_to_revision(@changeset.previous.revision, @project, :text => l(:label_previous)) %> + <%= link_to_revision(@changeset.previous, @project, :text => l(:label_previous)) %> <% else -%> <%= l(:label_previous) %> <% end -%> | <% unless @changeset.next.nil? -%> - <%= link_to_revision(@changeset.next.revision, @project, :text => l(:label_next)) %> + <%= link_to_revision(@changeset.next, @project, :text => l(:label_next)) %> <% else -%> <%= l(:label_next) %> <% end -%> @@ -19,7 +19,7 @@ <% end %>
    -

    <%= l(:label_revision) %> <%= format_revision(@changeset.revision) %>

    +

    <%= l(:label_revision) %> <%= format_revision(@changeset) %>

    <% if @changeset.scmid %>ID: <%= @changeset.scmid %>
    <% end %> <%= authoring(@changeset.committed_on, @changeset.author) %>

    @@ -45,7 +45,7 @@
  • <%= l(:label_deleted) %>
  • -

    <%= link_to(l(:label_view_diff), :action => 'diff', :id => @project, :path => "", :rev => @changeset.revision) if @changeset.changes.any? %>

    +

    <%= link_to(l(:label_view_diff), :action => 'diff', :id => @project, :path => "", :rev => @changeset.identifier) if @changeset.changes.any? %>

    <%= render_changeset_changes %> From 0bfd4f31afa5ce6e86670d0a676726d81b769056 Mon Sep 17 00:00:00 2001 From: Toshi MARUYAMA Date: Mon, 18 Oct 2010 20:33:24 +0900 Subject: [PATCH 10/60] helper: use scmid for "commit:xxx" link if available scmid is more solid than revision number. Signed-off-by: Yuya Nishihara Tested-on: Redmine trunk r3864 --- app/helpers/application_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 67d83754c9c..781a8952180 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -653,7 +653,7 @@ def parse_redmine_links(text, project, obj, attr, only_path, options) 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 From 46c9307ee8c98b5cea6dd8aeb2cc28acf669af22 Mon Sep 17 00:00:00 2001 From: Toshi MARUYAMA Date: Mon, 18 Oct 2010 20:33:24 +0900 Subject: [PATCH 11/60] activity: use scmid and format_revision if possible Signed-off-by: Yuya Nishihara Tested-on: Redmine trunk r3864 --- app/models/changeset.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/changeset.rb b/app/models/changeset.rb index a9cc49530f4..1c4b961979b 100644 --- a/app/models/changeset.rb +++ b/app/models/changeset.rb @@ -23,10 +23,10 @@ class Changeset < ActiveRecord::Base 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)} #{o.format_identifier}" + (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}, From e699366cd8f0bb48846dac0c4355d53c9478b8e4 Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Fri, 2 Apr 2010 00:05:36 +0900 Subject: [PATCH 12/60] repository: don't truncate revision string of input box repository/show does not truncate it. repository/revision should be the same. Signed-off-by: Yuya Nishihara Tested-on: Redmine trunk r3864 --- app/views/repositories/revision.rhtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/repositories/revision.rhtml b/app/views/repositories/revision.rhtml index fc942f4c246..1f3f611a123 100644 --- a/app/views/repositories/revision.rhtml +++ b/app/views/repositories/revision.rhtml @@ -14,7 +14,7 @@ »  <% form_tag({:controller => 'repositories', :action => 'revision', :id => @project, :rev => nil}, :method => :get) do %> - <%= text_field_tag 'rev', @rev[0,8], :size => 8 %> + <%= text_field_tag 'rev', @rev, :size => 8 %> <%= submit_tag 'OK', :name => nil %> <% end %>
    From 221fa00c5c7ad612716095d45bc848ab07455d37 Mon Sep 17 00:00:00 2001 From: Toshi MARUYAMA Date: Mon, 18 Oct 2010 20:33:24 +0900 Subject: [PATCH 13/60] changeset: prefer hash id than revision number for auto-close issue text All DVCS backend use scmid as hash id, and it's preferable than revision number. Tested-on: Redmine trunk r3864 --- app/models/changeset.rb | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/app/models/changeset.rb b/app/models/changeset.rb index 1c4b961979b..9e64a95b2d4 100644 --- a/app/models/changeset.rb +++ b/app/models/changeset.rb @@ -65,6 +65,15 @@ def format_identifier identifier[0, 8] end end + + # Returns the identifier for wiki, "rN" or "commit:ABCDEF" + def format_wiki_identifier + if scmid # hash-like + "commit:#{scmid}" + else # numeric + "r#{revision}" + end + end def comments=(comment) write_attribute(:comments, Changeset.normalize_comments(comment)) @@ -126,11 +135,8 @@ def scan_comment_for_issue_ids 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, format_wiki_identifier)) issue.status = fix_status unless Setting.commit_fix_done_ratio.blank? issue.done_ratio = Setting.commit_fix_done_ratio.to_i From e63463e31b48699c258bd70988951452b615a028 Mon Sep 17 00:00:00 2001 From: Toshi MARUYAMA Date: Mon, 18 Oct 2010 20:33:24 +0900 Subject: [PATCH 14/60] issue: show scmid on related revisions Signed-off-by: Yuya Nishihara Tested-on: Redmine trunk r3864 --- app/controllers/issues_controller.rb | 2 ++ app/views/issues/_changesets.rhtml | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index 67488b753ef..f1cdb7d2830 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -44,6 +44,8 @@ class IssuesController < ApplicationController include AttachmentsHelper helper :queries include QueriesHelper + helper :repositories + include RepositoriesHelper helper :sort include SortHelper include IssuesHelper diff --git a/app/views/issues/_changesets.rhtml b/app/views/issues/_changesets.rhtml index 52cd60ff571..d5a292896cc 100644 --- a/app/views/issues/_changesets.rhtml +++ b/app/views/issues/_changesets.rhtml @@ -1,7 +1,7 @@ <% changesets.each do |changeset| %>
    -

    <%= link_to("#{l(:label_revision)} #{changeset.revision}", - :controller => 'repositories', :action => 'revision', :id => changeset.project, :rev => changeset.revision) %>
    +

    <%= link_to_revision(changeset, changeset.project, + :text => "#{l(:label_revision)} #{changeset.format_identifier}") %>
    <%= authoring(changeset.committed_on, changeset.author) %>

    <%= textilizable(changeset, :comments) %> From e66149d0c2812917b1c826903d53fcf875428b75 Mon Sep 17 00:00:00 2001 From: Toshi MARUYAMA Date: Mon, 18 Oct 2010 20:33:24 +0900 Subject: [PATCH 15/60] repository: catch CommandFailed during bulk fetch_changesets --- app/models/repository.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index dee705c97d3..ea7c5994e99 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -172,7 +172,11 @@ def find_committer_user(committer) 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 From 13a0410ba69a7b7bda2c80582a08ee0165b854d9 Mon Sep 17 00:00:00 2001 From: Alessio Franceschelli Date: Sun, 28 Mar 2010 19:15:09 +0900 Subject: [PATCH 16/60] mercurial/redminehelper: helper extension to fetch info from hg repo [DRAFT] Downloaded http://www.redmine.org/attachments/3395/overhaul.py as redminehelper.py Then, untabified and removed trailing white spaces. --- extra/mercurial/redminehelper.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 extra/mercurial/redminehelper.py diff --git a/extra/mercurial/redminehelper.py b/extra/mercurial/redminehelper.py new file mode 100644 index 00000000000..0d6cd111f6d --- /dev/null +++ b/extra/mercurial/redminehelper.py @@ -0,0 +1,29 @@ +# redminehelper: draft extension for Mercurial +# it's a draft to show a possible way to explore repository by the Redmine overhaul patch +# see: http://www.redmine.org/issues/4455 +# +# Copyright 2010 Alessio Franceschelli (alefranz.net) +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +'''command to list revision of each file +''' + +from mercurial import cmdutil, commands +from mercurial.i18n import _ + +def overhaul(ui, repo, rev=None, **opts): + mf = repo[rev].manifest() + for f in repo[rev]: + try: + fctx = repo.filectx(f, fileid=mf[f]) + ctx = fctx.changectx() + ui.write('%s\t%d\t%s\n' % + (ctx,fctx.size(),f)) + except LookupError: + pass + +cmdtable = { + 'overhaul': (overhaul,commands.templateopts, _('hg overhaul [rev]')) +} From cbabb6d64e2678d419b230ef6c23467533c1607c Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Sun, 28 Mar 2010 19:15:09 +0900 Subject: [PATCH 17/60] mercurial/redminehelper: helper extension to reduce the number of hg calls --- extra/mercurial/redminehelper.py | 86 +++++++++++++++++++++++++++----- 1 file changed, 74 insertions(+), 12 deletions(-) diff --git a/extra/mercurial/redminehelper.py b/extra/mercurial/redminehelper.py index 0d6cd111f6d..6044e1c446c 100644 --- a/extra/mercurial/redminehelper.py +++ b/extra/mercurial/redminehelper.py @@ -1,8 +1,9 @@ -# redminehelper: draft extension for Mercurial +# redminehelper: Redmine helper extension for Mercurial # it's a draft to show a possible way to explore repository by the Redmine overhaul patch # see: http://www.redmine.org/issues/4455 # # Copyright 2010 Alessio Franceschelli (alefranz.net) +# Copyright 2010 Yuya Nishihara # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. @@ -10,20 +11,81 @@ '''command to list revision of each file ''' -from mercurial import cmdutil, commands -from mercurial.i18n import _ +import re, time +from mercurial import cmdutil, commands, node, error -def overhaul(ui, repo, rev=None, **opts): +SPECIAL_TAGS = ('tip',) + +def rhsummary(ui, repo, **opts): + """output the summary of the repository""" + # see mercurial/commands.py:tip + ui.write(':tip: rev node\n') + tipctx = repo[len(repo) - 1] + ui.write('%d %s\n' % (tipctx.rev(), tipctx)) + + # see mercurial/commands.py:root + ui.write(':root: path\n') + ui.write(repo.root + '\n') + + # see mercurial/commands.py:tags + ui.write(':tags: rev node name\n') + for t, n in reversed(repo.tagslist()): + if t in SPECIAL_TAGS: + continue + try: + r = repo.changelog.rev(n) + except error.LookupError: + r = -1 + ui.write('%d %s %s\n' % (r, node.short(n), t)) + + # see mercurial/commands.py:branches + def iterbranches(): + for t, n in repo.branchtags().iteritems(): + yield t, n, repo.changelog.rev(n) + + ui.write(':branches: rev node name\n') + for t, n, r in sorted(iterbranches(), key=lambda e: e[2], reverse=True): + if repo.lookup(r) in repo.branchheads(t, closed=False): + ui.write('%d %s %s\n' % (r, node.short(n), t)) # only open branch + +def rhentries(ui, repo, path='', **opts): + """output the entries of the specified directory""" + rev = opts.get('rev') + pathprefix = (path.rstrip('/') + '/').lstrip('/') + + # TODO: clean up + dirs, files = {}, {} mf = repo[rev].manifest() for f in repo[rev]: - try: - fctx = repo.filectx(f, fileid=mf[f]) - ctx = fctx.changectx() - ui.write('%s\t%d\t%s\n' % - (ctx,fctx.size(),f)) - except LookupError: - pass + if not f.startswith(pathprefix): + continue + + name = re.sub(r'/.*', '', f[len(pathprefix):]) + if '/' in f[len(pathprefix):]: + dirs[name] = (name,) + else: + try: + fctx = repo.filectx(f, fileid=mf[f]) + ctx = fctx.changectx() + tm, tzoffset = ctx.date() + localtime = int(tm) + tzoffset - time.timezone + files[name] = (ctx.rev(), node.short(ctx.node()), localtime, + fctx.size(), name) + except LookupError: # TODO: when this occurs? + pass + + ui.write(':dirs: name\n') + for n, v in sorted(dirs.iteritems(), key=lambda e: e[0]): + ui.write(' '.join(v) + '\n') + + ui.write(':files: rev node time size name\n') + for n, v in sorted(files.iteritems(), key=lambda e: e[0]): + ui.write(' '.join(str(e) for e in v) + '\n') + cmdtable = { - 'overhaul': (overhaul,commands.templateopts, _('hg overhaul [rev]')) + 'rhsummary': (rhsummary, [], 'hg rhsummary'), + 'rhentries': (rhentries, + [('r', 'rev', '', 'show the specified revision')], + 'hg rhentries [path]'), } From 1786639fc192264f307dcb838610395876524dd1 Mon Sep 17 00:00:00 2001 From: Toshi MARUYAMA Date: Mon, 18 Oct 2010 20:33:24 +0900 Subject: [PATCH 18/60] imported patch for-yuya/for-yuya.diff --- extra/mercurial/redminehelper.py | 9 +++++++-- test/unit/repository_mercurial_test.rb | 11 +++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/extra/mercurial/redminehelper.py b/extra/mercurial/redminehelper.py index 6044e1c446c..66cbab06cc8 100644 --- a/extra/mercurial/redminehelper.py +++ b/extra/mercurial/redminehelper.py @@ -8,6 +8,11 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. +# This is Mercurial extension for Redmine Mercurial adapter. +# http://mercurial.selenic.com/wiki/WritingExtensions +# You can run with following command. +# $ hg --config extensions.redminehelper=redminehelper.py rhsummary + '''command to list revision of each file ''' @@ -68,8 +73,8 @@ def rhentries(ui, repo, path='', **opts): fctx = repo.filectx(f, fileid=mf[f]) ctx = fctx.changectx() tm, tzoffset = ctx.date() - localtime = int(tm) + tzoffset - time.timezone - files[name] = (ctx.rev(), node.short(ctx.node()), localtime, + utctime = int(tm) + files[name] = (ctx.rev(), node.short(ctx.node()), utctime, fctx.size(), name) except LookupError: # TODO: when this occurs? pass diff --git a/test/unit/repository_mercurial_test.rb b/test/unit/repository_mercurial_test.rb index 1773631e943..163c219df65 100644 --- a/test/unit/repository_mercurial_test.rb +++ b/test/unit/repository_mercurial_test.rb @@ -54,6 +54,17 @@ def test_entries assert_equal 2, @repository.entries("sources", '400bb8672109').size assert_equal 1, @repository.entries("sources", 3).size assert_equal 1, @repository.entries("sources", 'b3a615152df8').size + + # TODO file size + ent_src_rev2 = @repository.entries("sources", 2) + assert_equal ent_src_rev2[0].name, "watchers_controller.rb" + # 2007-12-14 10:25:20 +0100 + assert_equal ent_src_rev2[0].lastrev.time, Time.gm(2007, 12, 14, 9, 25, 20) + + ent_root_rev5 = @repository.entries("", 5) + assert_equal 3, ent_root_rev5.size + assert_equal ent_root_rev5[2].name, "README" + assert_equal ent_root_rev5[2].lastrev.time, Time.gm(1970, 1, 1, 0, 0, 0) end def test_locate_on_outdated_repository From 1e40760fd67e658aab56b0ae1b94d481b5df5850 Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Sun, 28 Mar 2010 19:15:09 +0900 Subject: [PATCH 19/60] mercurial: introduced helper method to run hg command Then, the following methods use it: cat. I intend to redesign the others. --- lib/redmine/scm/adapters/mercurial_adapter.rb | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/lib/redmine/scm/adapters/mercurial_adapter.rb b/lib/redmine/scm/adapters/mercurial_adapter.rb index 16d28ce1991..cede75dc4e0 100644 --- a/lib/redmine/scm/adapters/mercurial_adapter.rb +++ b/lib/redmine/scm/adapters/mercurial_adapter.rb @@ -24,10 +24,14 @@ class MercurialAdapter < AbstractAdapter # Mercurial executable name HG_BIN = "hg" + HG_HELPER_EXT = "#{RAILS_ROOT}/extra/mercurial/redminehelper.py" TEMPLATES_DIR = File.dirname(__FILE__) + "/mercurial" TEMPLATE_NAME = "hg-template" TEMPLATE_EXTENSION = "tmpl" + # raised if hg command exited with error, e.g. unknown revision. + class HgCommandAborted < CommandFailed; end + class << self def client_version @client_version ||= hgversion @@ -168,16 +172,12 @@ def diff(path, identifier_from, identifier_to=nil) end def cat(path, identifier=nil) - cmd = "#{HG_BIN} -R #{target('')} cat" - cmd << " -r " + (identifier ? identifier.to_s : "tip") - cmd << " #{target(path)}" - cat = nil - shellout(cmd) do |io| + hg 'cat', '-r', hgrev(identifier), without_leading_slash(path) do |io| io.binmode - cat = io.read + io.read end - return nil if $? && $?.exitstatus != 0 - cat + rescue HgCommandAborted + nil # means not found end def annotate(path, identifier=nil) @@ -204,6 +204,25 @@ def format_identifier "#{revision}:#{scmid}" end end + + # Runs 'hg' command with the given args + def hg(*args, &block) + full_args = [HG_BIN, '--cwd', url, '--encoding', 'utf-8'] + full_args << '--config' << "extensions.redminehelper=#{HG_HELPER_EXT}" + full_args += args + ret = shellout(full_args.map { |e| shell_quote e.to_s }.join(' '), &block) + if $? && $?.exitstatus != 0 + raise HgCommandAborted, "hg exited with non-zero status: #{$?.exitstatus}" + end + ret + end + private :hg + + # Returns correct revision identifier + def hgrev(identifier) + identifier.blank? ? 'tip' : identifier.to_s + end + private :hgrev end end end From 85755de7a2ae6d7155c31a91b27502f5959946a4 Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Sun, 28 Mar 2010 19:15:09 +0900 Subject: [PATCH 20/60] mercurial: rewrite scm.diff to generate correct changeset diff --- lib/redmine/scm/adapters/mercurial_adapter.rb | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/lib/redmine/scm/adapters/mercurial_adapter.rb b/lib/redmine/scm/adapters/mercurial_adapter.rb index cede75dc4e0..781c94aab4f 100644 --- a/lib/redmine/scm/adapters/mercurial_adapter.rb +++ b/lib/redmine/scm/adapters/mercurial_adapter.rb @@ -153,22 +153,19 @@ def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}) end def diff(path, identifier_from, identifier_to=nil) - path ||= '' + hg_args = ['diff', '--nodates'] if identifier_to - identifier_to = identifier_to.to_i + hg_args << '-r' << hgrev(identifier_to) << '-r' << hgrev(identifier_from) else - identifier_to = identifier_from.to_i - 1 + hg_args << '-c' << hgrev(identifier_from) end - cmd = "#{HG_BIN} -R #{target('')} diff -r #{identifier_to} -r #{identifier_from} --nodates" - cmd << " -I #{target(path)}" unless path.empty? - diff = [] - shellout(cmd) do |io| - io.each_line do |line| - diff << line - end + hg_args << without_leading_slash(path) unless path.blank? + + hg *hg_args do |io| + io.collect end - return nil if $? && $?.exitstatus != 0 - diff + rescue HgCommandAborted + nil # means not found end def cat(path, identifier=nil) From d6dc33ede85be14cd8c6dab6b4378a3894da3b5e Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Tue, 23 Mar 2010 23:14:00 +0900 Subject: [PATCH 21/60] mercurial: rewrite scm.annotate * Now fetches nodeid. * Revision.identifier is omitted. This means to use scmid instead. --- lib/redmine/scm/adapters/mercurial_adapter.rb | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/lib/redmine/scm/adapters/mercurial_adapter.rb b/lib/redmine/scm/adapters/mercurial_adapter.rb index 781c94aab4f..b7050b93241 100644 --- a/lib/redmine/scm/adapters/mercurial_adapter.rb +++ b/lib/redmine/scm/adapters/mercurial_adapter.rb @@ -178,21 +178,18 @@ def cat(path, identifier=nil) end def annotate(path, identifier=nil) - path ||= '' - cmd = "#{HG_BIN} -R #{target('')}" - cmd << " annotate -n -u" - cmd << " -r " + (identifier ? identifier.to_s : "tip") - cmd << " -r #{identifier.to_i}" if identifier - cmd << " #{target(path)}" blame = Annotate.new - shellout(cmd) do |io| - io.each_line do |line| - next unless line =~ %r{^([^:]+)\s(\d+):(.*)$} - blame.add_line($3.rstrip, Revision.new(:identifier => $2.to_i, :author => $1.strip)) + hg 'annotate', '-ncu', '-r', hgrev(identifier), without_leading_slash(path) do |io| + io.each do |line| + next unless line =~ %r{^([^:]+)\s(\d+)\s([0-9a-f]+):(.*)$} + r = Revision.new(:author => $1.strip, :revision => $2, :scmid => $3, + :identifier => $3) + blame.add_line($4.rstrip, r) end end - return nil if $? && $?.exitstatus != 0 blame + rescue HgCommandAborted + nil # means not found or cannot be annotated end class Revision < Redmine::Scm::Adapters::Revision From d1f18f6a4c2b649f3b3f169e151b855fa7ba4d92 Mon Sep 17 00:00:00 2001 From: Toshi MARUYAMA Date: Mon, 18 Oct 2010 20:33:25 +0900 Subject: [PATCH 22/60] imported patch hg/annotate-test.diff --- .../repositories_mercurial_controller_test.rb | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/test/functional/repositories_mercurial_controller_test.rb b/test/functional/repositories_mercurial_controller_test.rb index 59ece70ea5b..4537912c1d1 100644 --- a/test/functional/repositories_mercurial_controller_test.rb +++ b/test/functional/repositories_mercurial_controller_test.rb @@ -133,12 +133,33 @@ def test_annotate get :annotate, :id => 3, :path => ['sources', 'watchers_controller.rb'] assert_response :success assert_template 'annotate' - # Line 23, revision 4 - assert_tag :tag => 'th', :content => /23/, - :sibling => { :tag => 'td', :child => { :tag => 'a', :content => /4/ } }, - :sibling => { :tag => 'td', :content => /jsmith/ }, + # Line 23, revision 4:def6d2f1254a + assert_tag :tag => 'th', + :content => '23', + :attributes => { :class => 'line-num' }, + :sibling => + { + :tag => 'td', + :attributes => { :class => 'revision' }, + :child => { :tag => 'a', :content => '4' } + # :child => { :tag => 'a', :content => /4:def6d2f1/ } + } + assert_tag :tag => 'th', + :content => '23', + :attributes => { :class => 'line-num' }, + :sibling => + { + :tag => 'td' , + :content => 'jsmith' , + :attributes => { :class => 'author' }, + + } + assert_tag :tag => 'th', + :content => '23', + :attributes => { :class => 'line-num' }, :sibling => { :tag => 'td', :content => /watcher =/ } end + else puts "Mercurial test repository NOT FOUND. Skipping functional tests !!!" def test_fake; assert true end From d7acbb5ffb0fff42cb38d6c6ba715b7987d2fdba Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Sun, 28 Mar 2010 19:15:09 +0900 Subject: [PATCH 23/60] mercurial: rewrite MercurialAdapter.revisions as iterator Reimplemented .revisions as .each_revision. Also added "require 'rexml/document'" to suppress "uninitialized constant" error when running as rake task. --- .../adapters/mercurial/hg-template-0.9.5.tmpl | 2 +- .../adapters/mercurial/hg-template-1.0.tmpl | 2 +- lib/redmine/scm/adapters/mercurial_adapter.rb | 85 +++++++++---------- 3 files changed, 44 insertions(+), 45 deletions(-) diff --git a/lib/redmine/scm/adapters/mercurial/hg-template-0.9.5.tmpl b/lib/redmine/scm/adapters/mercurial/hg-template-0.9.5.tmpl index b3029e6ff07..bf5ccdbe3dc 100644 --- a/lib/redmine/scm/adapters/mercurial/hg-template-0.9.5.tmpl +++ b/lib/redmine/scm/adapters/mercurial/hg-template-0.9.5.tmpl @@ -9,4 +9,4 @@ file_del = '{file_del|escape}\n' file_copy = '{name|urlescape}\n' tag = '{tag|escape}\n' header='\n\n\n' -# footer="" \ No newline at end of file +footer='' diff --git a/lib/redmine/scm/adapters/mercurial/hg-template-1.0.tmpl b/lib/redmine/scm/adapters/mercurial/hg-template-1.0.tmpl index b6cc7a78692..51dd0503749 100644 --- a/lib/redmine/scm/adapters/mercurial/hg-template-1.0.tmpl +++ b/lib/redmine/scm/adapters/mercurial/hg-template-1.0.tmpl @@ -9,4 +9,4 @@ file_del = '{file_del|escape}\n' file_copy = '{name|urlescape}\n' tag = '{tag|escape}\n' header='\n\n\n' -# footer="" +footer='' diff --git a/lib/redmine/scm/adapters/mercurial_adapter.rb b/lib/redmine/scm/adapters/mercurial_adapter.rb index b7050b93241..6cde959349f 100644 --- a/lib/redmine/scm/adapters/mercurial_adapter.rb +++ b/lib/redmine/scm/adapters/mercurial_adapter.rb @@ -16,6 +16,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. require 'redmine/scm/adapters/abstract_adapter' +require 'rexml/document' module Redmine module Scm @@ -103,53 +104,51 @@ def entries(path=nil, identifier=nil) entries.sort_by_name end - # Fetch the revisions by using a template file that - # makes Mercurial produce a xml output. - def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}) + # TODO: is this api necessary? + def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}) revisions = Revisions.new - cmd = "#{HG_BIN} --debug --encoding utf8 -R #{target('')} log -C --style #{shell_quote self.class.template_path}" - if identifier_from && identifier_to - cmd << " -r #{identifier_from.to_i}:#{identifier_to.to_i}" - elsif identifier_from - cmd << " -r #{identifier_from.to_i}:" - end - cmd << " --limit #{options[:limit].to_i}" if options[:limit] - cmd << " #{path}" if path - shellout(cmd) do |io| + each_revision { |e| revisions << e } + revisions + end + + # Iterates the revisions by using a template file that + # makes Mercurial produce a xml output. + def each_revision(path=nil, identifier_from=nil, identifier_to=nil, options={}) + hg_args = ['log', '--debug', '-C', '--style', self.class.template_path] + hg_args << '-r' << "#{hgrev(identifier_from)}:#{hgrev(identifier_to)}" + hg_args << '--limit' << options[:limit] if options[:limit] + hg_args << without_leading_slash(path) unless path.blank? + doc = hg(*hg_args) do |io| + s = io.read begin - # HG doesn't close the XML Document... - doc = REXML::Document.new(io.read << "") - doc.elements.each("log/logentry") do |logentry| - paths = [] - copies = logentry.get_elements('paths/path-copied') - logentry.elements.each("paths/path") do |path| - # Detect if the added file is a copy - if path.attributes['action'] == 'A' and c = copies.find{ |e| e.text == path.text } - from_path = c.attributes['copyfrom-path'] - from_rev = logentry.attributes['revision'] - end - paths << {:action => path.attributes['action'], - :path => "/#{path.text}", - :from_path => from_path ? "/#{from_path}" : nil, - :from_revision => from_rev ? from_rev : nil - } - end - paths.sort! { |x,y| x[:path] <=> y[:path] } - - revisions << Revision.new({:identifier => logentry.attributes['revision'], - :scmid => logentry.attributes['node'], - :author => (logentry.elements['author'] ? logentry.elements['author'].text : ""), - :time => Time.parse(logentry.elements['date'].text).localtime, - :message => logentry.elements['msg'].text, - :paths => paths - }) - end - rescue - logger.debug($!) + REXML::Document.new(s) + rescue REXML::ParseException + # Mercurial < 1.5 does not support footer template for '' + REXML::Document.new(s + '') end end - return nil if $? && $?.exitstatus != 0 - revisions + + doc.each_element('log/logentry') do |le| + cpalist = le.get_elements('paths/path-copied').map do |e| + [e.text, e.attributes['copyfrom-path']] + end + cpmap = Hash[*cpalist.flatten] + + paths = le.get_elements('paths/path').map do |e| + {:action => e.attributes['action'], :path => with_leading_slash(e.text), + :from_path => (cpmap.member?(e.text) ? with_leading_slash(cpmap[e.text]) : nil), + :from_revision => (cpmap.member?(e.text) ? le.attributes['revision'] : nil)} + end.sort { |a, b| a[:path] <=> b[:path] } + + yield Revision.new(:identifier => le.attributes['revision'], + :revision => le.attributes['revision'], + :scmid => le.attributes['node'], + :author => (le.elements['author'].text rescue ''), + :time => Time.parse(le.elements['date'].text).localtime, + :message => le.elements['msg'].text, + :paths => paths) + end + self end def diff(path, identifier_from, identifier_to=nil) From 6994ffc4f02d0eadcc2a2573be04c9aa85cb0ce8 Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Sun, 28 Mar 2010 19:15:09 +0900 Subject: [PATCH 24/60] mercurial: rewrite scm.info by using helper ext --- lib/redmine/scm/adapters/mercurial_adapter.rb | 48 ++++++++++++++----- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/lib/redmine/scm/adapters/mercurial_adapter.rb b/lib/redmine/scm/adapters/mercurial_adapter.rb index 6cde959349f..491d7e751ec 100644 --- a/lib/redmine/scm/adapters/mercurial_adapter.rb +++ b/lib/redmine/scm/adapters/mercurial_adapter.rb @@ -65,19 +65,17 @@ def template_path_for(version) end def info - cmd = "#{HG_BIN} -R #{target('')} root" - root_url = nil - shellout(cmd) do |io| - root_url = io.read - end - return nil if $? && $?.exitstatus != 0 - info = Info.new({:root_url => root_url.chomp, - :lastrev => revisions(nil,nil,nil,{:limit => 1}).last - }) - info - rescue CommandFailed - return nil + tip = summary['tip'].first + Info.new(:root_url => summary['root'].first['path'], + :lastrev => Revision.new(:identifier => tip['rev'], + :revision => tip['rev'], + :scmid => tip['node'])) end + + def summary + @summary ||= fetchg 'rhsummary' + end + private :summary def entries(path=nil, identifier=nil) path ||= '' @@ -211,6 +209,32 @@ def hg(*args, &block) end private :hg + # Runs 'hg' helper, then parses output to return + def fetchg(*args) + # command output example: + # :tip: rev node + # 100 abcdef012345 + # :tags: rev node name + # 100 abcdef012345 tip + # ... + data = Hash.new { |h, k| h[k] = [] } + hg(*args) do |io| + key, attrs = nil, nil + io.each do |line| + next if line.chomp.empty? + if /^:(\w+): ([\w ]+)/ =~ line + key = $1 + attrs = $2.split(/ /) + elsif key + alist = attrs.zip(line.chomp.split(/ /, attrs.size)) + data[key] << Hash[*alist.flatten] + end + end + end + data + end + private :fetchg + # Returns correct revision identifier def hgrev(identifier) identifier.blank? ? 'tip' : identifier.to_s From 031063ae52da4d4c32198ec672a36ca4d338cbf1 Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Sun, 28 Mar 2010 19:15:09 +0900 Subject: [PATCH 25/60] mercurial: cleaned up fetch_changesets --- app/models/repository/mercurial.rb | 48 +++++++++++------------------- 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/app/models/repository/mercurial.rb b/app/models/repository/mercurial.rb index 23196eb831f..1a4ad30a13a 100644 --- a/app/models/repository/mercurial.rb +++ b/app/models/repository/mercurial.rb @@ -24,6 +24,8 @@ class Repository::Mercurial < Repository 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 @@ -89,38 +91,24 @@ def latest_changesets(path, rev, limit=10) 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 + 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 + self end end From f9f93c5cd394c19e38e297ae370fc29f4a717f57 Mon Sep 17 00:00:00 2001 From: Brent Sowers Date: Mon, 27 Sep 2010 20:40:22 -0400 Subject: [PATCH 26/60] Added Rubymine .idea folder and emacs ~ files to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 853230d740e..c3d0b501e58 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ /tmp/test/* /vendor/rails *.rbc +/.idea +*~ From 5768291aff053294588c163c0ee592e154d4b98d Mon Sep 17 00:00:00 2001 From: Brent Sowers Date: Mon, 27 Sep 2010 21:53:42 -0400 Subject: [PATCH 27/60] Started making changes to fix Bazaar second/third level revisions not showing up --- app/helpers/application_helper.rb | 2 +- app/models/repository/bazaar.rb | 111 ++++++++++++++------- lib/redmine/scm/adapters/bazaar_adapter.rb | 52 ++++++---- 3 files changed, 109 insertions(+), 56 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 6ba40eb4511..ca0551488f2 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -119,7 +119,7 @@ def link_to_attachment(attachment, options={}) def link_to_revision(revision, project, options={}) text = options.delete(:text) || format_revision(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 => h(revision)}, :title => l(:label_revision_id, revision)) end def link_to_project(project, options={}) diff --git a/app/models/repository/bazaar.rb b/app/models/repository/bazaar.rb index ec953bd4542..10c5ab83699 100644 --- a/app/models/repository/bazaar.rb +++ b/app/models/repository/bazaar.rb @@ -52,40 +52,83 @@ def entries(path=nil, identifier=nil) end 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 : 0 +# # latest revision in the repository +# scm_revision = scm_info.lastrev.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 200 +# identifier_to = [identifier_from + 199, scm_revision].min +# revisions = scm.revisions('', identifier_to, identifier_from, :with_paths => true) +# transaction do +# revisions.reverse_each do |revision| +# changeset = Changeset.create(:repository => self, +# :revision => revision.identifier, +# :committer => revision.author, +# :committed_on => revision.time, +# :scmid => revision.scmid, +# :comments => revision.message) +# +# revision.paths.each do |change| +# Change.create(:changeset => changeset, +# :action => change[:action], +# :path => change[:path], +# :revision => change[:revision]) +# end +# end +# end unless revisions.nil? +# identifier_from = identifier_to + 1 +# end +# end +# end +# end + + # With SCM's that have a sequential commit numbering, redmine is able to be + # clever and only fetch changesets going forward from the most recent one + # it knows about. However, with bazaar, you never know if people have merged + # commits into the middle of the repository history, so we should parse + # the entire log. Since it's way too slow for large repositories, we only + # parse 1 week before the last known commit. + # The repository can still be fully reloaded by calling #clear_changesets + # before fetching changesets (eg. for offline resync) def fetch_changesets - scm_info = scm.info - if scm_info - # latest revision found in database - db_revision = latest_changeset ? latest_changeset.revision.to_i : 0 - # latest revision in the repository - scm_revision = scm_info.lastrev.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 200 - identifier_to = [identifier_from + 199, scm_revision].min - revisions = scm.revisions('', identifier_to, identifier_from, :with_paths => true) - transaction do - revisions.reverse_each do |revision| - changeset = Changeset.create(:repository => self, - :revision => revision.identifier, - :committer => revision.author, - :committed_on => revision.time, - :scmid => revision.scmid, - :comments => revision.message) - - revision.paths.each do |change| - Change.create(:changeset => changeset, - :action => change[:action], - :path => change[:path], - :revision => change[:revision]) - end - end - end unless revisions.nil? - identifier_from = identifier_to + 1 - end - end - end + 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) + return if revisions.nil? || revisions.empty? + + recent_changesets = changesets.find(:all, :conditions => ['committed_on >= ?', since]) + + # Clean out revisions that are no longer in git + recent_changesets.each {|c| c.destroy unless revisions.detect {|r| r.scmid.to_s == c.scmid.to_s }} + + # Subtract revisions that redmine already knows about + recent_revisions = recent_changesets.map{|c| c.scmid} + revisions.reject!{|r| recent_revisions.include?(r.scmid)} + + # Save the remaining ones to the database + revisions.each{|r| r.save(self)} unless revisions.nil? end + + def latest_changesets(path,rev,limit=10) + revisions = scm.revisions(path, nil, rev, :limit => limit, :all => false) + return [] if revisions.nil? || revisions.empty? + + changesets.find( + :all, + :conditions => [ + "scmid IN (?)", + revisions.map!{|c| c.scmid} + ], + :order => 'committed_on DESC' + ) + end + end diff --git a/lib/redmine/scm/adapters/bazaar_adapter.rb b/lib/redmine/scm/adapters/bazaar_adapter.rb index 3c6bdf542bc..00a3019b608 100644 --- a/lib/redmine/scm/adapters/bazaar_adapter.rb +++ b/lib/redmine/scm/adapters/bazaar_adapter.rb @@ -51,7 +51,7 @@ def entries(path=nil, identifier=nil) entries = Entries.new cmd = "#{BZR_BIN} ls -v --show-ids" identifier = -1 unless identifier && identifier.to_i > 0 - cmd << " -r#{identifier.to_i}" + cmd << " -r#{identifier}" cmd << " #{target(path)}" shellout(cmd) do |io| prefix = "#{url}/#{path}".gsub('\\', '/') @@ -74,35 +74,44 @@ def entries(path=nil, identifier=nil) def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}) path ||= '' - identifier_from = 'last:1' unless identifier_from and identifier_from.to_i > 0 - identifier_to = 1 unless identifier_to and identifier_to.to_i > 0 revisions = Revisions.new - cmd = "#{BZR_BIN} log -v --show-ids -r#{identifier_to.to_i}..#{identifier_from} #{target(path)}" + cmd = "#{BZR_BIN} log -n 0 -v --show-ids" + if options[:since] + cmd << " -r date:#{shell_quote(options[:since].strftime("%Y-%m-%d,%H:%M:%S"))}" + else + from = identifier_from ? "revid:#{identifier_from}" : "last:1" + to = identifier_to ? "revid:#{identifier_to}" : "1" + cmd << " -r #{to}..#{from}" + end + cmd << " -l #{options[:limit]} " if options[:limit] + cmd << " #{target(path)}" shellout(cmd) do |io| revision = nil parsing = nil io.each_line do |line| - if line =~ /^----/ + if line =~ /^\s*----/ revisions << revision if revision revision = Revision.new(:paths => [], :message => '') parsing = nil else next unless revision - if line =~ /^revno: (\d+)($|\s\[merge\]$)/ - revision.identifier = $1.to_i - elsif line =~ /^committer: (.+)$/ + if line =~ /^\s*revno: (\d+(\.\d+)*)($|\s\[merge\]$)/ + #revision.identifier = $1 + # I'll figure out what to do with this later + elsif line =~ /^\s*committer: (.+)$/ revision.author = $1.strip - elsif line =~ /^revision-id:(.+)$/ + elsif line =~ /^\s*revision-id:(.+)$/ revision.scmid = $1.strip - elsif line =~ /^timestamp: (.+)$/ + revision.identifier = revision.scmid + elsif line =~ /^\s*timestamp: (.+)$/ revision.time = Time.parse($1).localtime - elsif line =~ /^ -----/ + elsif line =~ /^ \s*-----/ # partial revisions parsing = nil unless parsing == 'message' - elsif line =~ /^(message|added|modified|removed|renamed):/ + elsif line =~ /^\s*(message|added|modified|removed|renamed):/ parsing = $1 - elsif line =~ /^ (.*)$/ + elsif line =~ /^ \s*(.*)$/ if parsing == 'message' revision.message << "#{$1}\n" else @@ -135,12 +144,13 @@ def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}) def diff(path, identifier_from, identifier_to=nil) path ||= '' - if identifier_to - identifier_to = identifier_to.to_i + cmd = "#{BZR_BIN} diff" + if identifier_to.nil? + cmd << " -c revid:#{identifier_from}" else - identifier_to = identifier_from.to_i - 1 + cmd << " -r revid:#{identifier_to}..revid:#{identifier_from}" end - cmd = "#{BZR_BIN} diff -r#{identifier_to}..#{identifier_from} #{target(path)}" + cmd << " #{target(path)}" diff = [] shellout(cmd) do |io| io.each_line do |line| @@ -153,7 +163,7 @@ def diff(path, identifier_from, identifier_to=nil) def cat(path, identifier=nil) cmd = "#{BZR_BIN} cat" - cmd << " -r#{identifier.to_i}" if identifier && identifier.to_i > 0 + cmd << " -r revid:#{identifier}" if identifier cmd << " #{target(path)}" cat = nil shellout(cmd) do |io| @@ -166,15 +176,15 @@ def cat(path, identifier=nil) def annotate(path, identifier=nil) cmd = "#{BZR_BIN} annotate --all" - cmd << " -r#{identifier.to_i}" if identifier && identifier.to_i > 0 + cmd << " -r revid:#{identifier}" if identifier cmd << " #{target(path)}" blame = Annotate.new shellout(cmd) do |io| author = nil identifier = nil io.each_line do |line| - next unless line =~ %r{^(\d+) ([^|]+)\| (.*)$} - blame.add_line($3.rstrip, Revision.new(:identifier => $1.to_i, :author => $2.strip)) + next unless line =~ %r{^(\d+(\.\d+)*) ([^|]+)\| (.*)$} + blame.add_line($3.rstrip, Revision.new(:identifier => $1, :author => $3.strip)) end end return nil if $? && $?.exitstatus != 0 From 14b0f586f40ef137811583da5aac661b177becdb Mon Sep 17 00:00:00 2001 From: Brent Sowers Date: Tue, 28 Sep 2010 08:28:56 -0400 Subject: [PATCH 28/60] Updated test bazaar repository to include merges --- .../repositories/bazaar_repository.tar.gz | Bin 9382 -> 15333 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/test/fixtures/repositories/bazaar_repository.tar.gz b/test/fixtures/repositories/bazaar_repository.tar.gz index 621c2f145e71c37be837b7bad3f0c9b28096a0ea..9900163b5321cffdd7d2cc49491c6ae6acc57ec9 100644 GIT binary patch literal 15333 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 Date: Tue, 28 Sep 2010 11:02:56 -0400 Subject: [PATCH 29/60] Changed all repository links to be safe (no . for Bazaar revisions), and updated Bazaar adapter to always specify revid in commands --- app/controllers/repositories_controller.rb | 2 +- app/helpers/application_helper.rb | 2 +- app/helpers/repositories_helper.rb | 13 ++++++++++--- app/views/repositories/_breadcrumbs.rhtml | 4 ++-- app/views/repositories/revision.rhtml | 2 +- lib/redmine/scm/adapters/bazaar_adapter.rb | 8 ++++---- 6 files changed, 19 insertions(+), 12 deletions(-) diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index b6dcc317343..fa5d3bd28ee 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -203,7 +203,7 @@ def find_repository (render_404; return false) unless @repository @path = params[:path].join('/') unless params[:path].nil? @path ||= '' - @rev = params[:rev].blank? ? @repository.default_branch : params[:rev].strip + @rev = params[:rev].blank? ? @repository.default_branch : params[:rev].strip.gsub(/_/, ".") @rev_to = params[:rev_to] rescue ActiveRecord::RecordNotFound render_404 diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index ca0551488f2..3398c51c4b3 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -119,7 +119,7 @@ def link_to_attachment(attachment, options={}) def link_to_revision(revision, project, options={}) text = options.delete(:text) || format_revision(revision) - link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => h(revision)}, :title => l(:label_revision_id, revision)) + link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => revision.gsub(/\./, "_")}, :title => l(:label_revision_id, revision)) end def link_to_project(project, options={}) diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb index 95962873541..53abc6ccff7 100644 --- a/app/helpers/repositories_helper.rb +++ b/app/helpers/repositories_helper.rb @@ -21,6 +21,13 @@ module RepositoriesHelper def format_revision(txt) txt.to_s[0,8] end + + # Some VCSes (Bazaar) have a . in the revision number, which causes problems + # when it's part of the URL in a link. Call this on a revision when + # including it in a link + def link_safe_rev(revision) + revision.gsub(/\./, "_") + end def truncate_at_line_break(text, length = 255) if text @@ -87,7 +94,7 @@ def render_changes_tree(tree) :action => 'show', :id => @project, :path => path_param, - :rev => @changeset.revision) + :rev => link_safe_rev(@changeset.revision)) output << "
  • #{text}
  • " output << render_changes_tree(s) elsif c = tree[file][:c] @@ -97,13 +104,13 @@ def render_changes_tree(tree) :action => 'entry', :id => @project, :path => path_param, - :rev => @changeset.revision) unless c.action == 'D' + :rev => link_safe_rev(@changeset.revision)) 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 => link_safe_rev(@changeset.revision)) + ') ' if c.action == 'M' text << ' ' + content_tag('span', c.from_path, :class => 'copied-from') unless c.from_path.blank? output << "
  • #{text}
  • " end diff --git a/app/views/repositories/_breadcrumbs.rhtml b/app/views/repositories/_breadcrumbs.rhtml index bab0ff33875..56fbcaabe7a 100644 --- a/app/views/repositories/_breadcrumbs.rhtml +++ b/app/views/repositories/_breadcrumbs.rhtml @@ -10,10 +10,10 @@ dirs.each do |dir| link_path << '/' unless link_path.empty? link_path << "#{dir}" %> - / <%= link_to h(dir), :action => 'show', :id => @project, :path => to_path_param(link_path), :rev => @rev %> + / <%= link_to h(dir), :action => 'show', :id => @project, :path => to_path_param(link_path), :rev => link_safe_rev(@rev) %> <% end %> <% if filename %> - / <%= link_to h(filename), :action => 'changes', :id => @project, :path => to_path_param("#{link_path}/#{filename}"), :rev => @rev %> + / <%= link_to h(filename), :action => 'changes', :id => @project, :path => to_path_param("#{link_path}/#{filename}"), :rev => link_safe_rev(@rev) %> <% end %> <%= "@ #{h revision}" if revision %> diff --git a/app/views/repositories/revision.rhtml b/app/views/repositories/revision.rhtml index 92597dff7c9..543dc9e4b6c 100644 --- a/app/views/repositories/revision.rhtml +++ b/app/views/repositories/revision.rhtml @@ -45,7 +45,7 @@
  • <%= l(:label_deleted) %>
  • -

    <%= link_to(l(:label_view_diff), :action => 'diff', :id => @project, :path => "", :rev => @changeset.revision) if @changeset.changes.any? %>

    +

    <%= link_to(l(:label_view_diff), :action => 'diff', :id => @project, :path => "", :rev => link_safe_rev(@changeset.revision)) if @changeset.changes.any? %>

    <%= render_changeset_changes %> diff --git a/lib/redmine/scm/adapters/bazaar_adapter.rb b/lib/redmine/scm/adapters/bazaar_adapter.rb index 00a3019b608..8cd13b0df76 100644 --- a/lib/redmine/scm/adapters/bazaar_adapter.rb +++ b/lib/redmine/scm/adapters/bazaar_adapter.rb @@ -30,7 +30,7 @@ def info cmd = "#{BZR_BIN} revno #{target('')}" info = nil shellout(cmd) do |io| - if io.read =~ %r{^(\d+)\r?$} + if io.read =~ %r{^(\d+(\.\d+)*)\r?$} info = Info.new({:root_url => url, :lastrev => Revision.new({ :identifier => $1 @@ -50,8 +50,8 @@ def entries(path=nil, identifier=nil) path ||= '' entries = Entries.new cmd = "#{BZR_BIN} ls -v --show-ids" - identifier = -1 unless identifier && identifier.to_i > 0 - cmd << " -r#{identifier}" + rev_spec = identifier ? "revid:#{identifier}" : "-1" + cmd << " -r #{rev_spec}" cmd << " #{target(path)}" shellout(cmd) do |io| prefix = "#{url}/#{path}".gsub('\\', '/') @@ -77,7 +77,7 @@ def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}) revisions = Revisions.new cmd = "#{BZR_BIN} log -n 0 -v --show-ids" if options[:since] - cmd << " -r date:#{shell_quote(options[:since].strftime("%Y-%m-%d,%H:%M:%S"))}" + cmd << " -r date:#{shell_quote(options[:since].strftime("%Y-%m-%d,%H:%M:%S"))}.." else from = identifier_from ? "revid:#{identifier_from}" : "last:1" to = identifier_to ? "revid:#{identifier_to}" : "1" From ec467fe54cb3d4e6afec645a48b6d48fcbb5b2bf Mon Sep 17 00:00:00 2001 From: Brent Sowers Date: Wed, 29 Sep 2010 14:54:46 -0400 Subject: [PATCH 30/60] Redmine now working successfully with Bazaar. Tests are failing, need to fix/update unit and functional tests --- app/controllers/repositories_controller.rb | 5 +++-- app/helpers/application_helper.rb | 20 +++++++++++++++++-- app/helpers/repositories_helper.rb | 10 ---------- app/views/issues/_changesets.rhtml | 4 ++-- .../repositories/_dir_list_content.rhtml | 2 +- app/views/repositories/_revisions.rhtml | 2 +- app/views/repositories/annotate.rhtml | 2 +- app/views/repositories/diff.rhtml | 2 +- app/views/repositories/revision.rhtml | 6 +++--- ...29174249_add_display_name_to_changesets.rb | 9 +++++++++ lib/redmine/scm/adapters/abstract_adapter.rb | 4 +++- lib/redmine/scm/adapters/bazaar_adapter.rb | 3 +-- 12 files changed, 43 insertions(+), 26 deletions(-) create mode 100644 db/migrate/20100929174249_add_display_name_to_changesets.rb diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index fa5d3bd28ee..3a686d304a8 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -168,7 +168,8 @@ def diff User.current.pref[:diff_type] = @diff_type User.current.preference.save end - + @start_changeset = @repository.find_changeset_by_name(@rev) + @end_changeset = @respository.find_changeset_by_name(@rev_to) if @rev_to @cache_key = "repositories/diff/#{@repository.id}/" + Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}") unless read_fragment(@cache_key) @diff = @repository.diff(@path, @rev, @rev_to) @@ -201,7 +202,7 @@ def find_repository @project = Project.find(params[:id]) @repository = @project.repository (render_404; return false) unless @repository - @path = params[:path].join('/') unless params[:path].nil? + @path = params[:path].join('/') unless params[:path].nil? || params[:path].empty? @path ||= '' @rev = params[:rev].blank? ? @repository.default_branch : params[:rev].strip.gsub(/_/, ".") @rev_to = params[:rev_to] diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 3398c51c4b3..96c8767f892 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -117,9 +117,10 @@ def link_to_attachment(attachment, options={}) # Options: # * :text - Link text (default to the formatted revision) def link_to_revision(revision, project, options={}) - text = options.delete(:text) || format_revision(revision) + revision_identifier = revision.revision.gsub(/\./, "_") + text = options.delete(:text) || (revision.respond_to?(:display_name) && revision.display_name ? revision.display_name : format_revision(revision.revision)) - link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => revision.gsub(/\./, "_")}, :title => l(:label_revision_id, revision)) + link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => revision_identifier}, :title => l(:label_revision_id, revision.revision)) end def link_to_project(project, options={}) @@ -176,6 +177,21 @@ def format_activity_description(text) h(truncate(text.to_s, :length => 120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')).gsub(/[\r\n]+/, "
    ") end + def changeset_name(changeset) + changeset.display_name ? changeset.display_name : format_revision(changeset.revision) + end + + def format_revision(txt) + txt.to_s[0,8] + end + + # Some VCSes (Bazaar) have a . in the revision number, which causes problems + # when it's part of the URL in a link. Call this on a revision when + # including it in a link + def link_safe_rev(revision) + revision.gsub(/\./, "_") + end + def format_version_name(version) if version.project == @project h(version) diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb index 53abc6ccff7..24154a12750 100644 --- a/app/helpers/repositories_helper.rb +++ b/app/helpers/repositories_helper.rb @@ -18,16 +18,6 @@ require 'iconv' module RepositoriesHelper - def format_revision(txt) - txt.to_s[0,8] - end - - # Some VCSes (Bazaar) have a . in the revision number, which causes problems - # when it's part of the URL in a link. Call this on a revision when - # including it in a link - def link_safe_rev(revision) - revision.gsub(/\./, "_") - end def truncate_at_line_break(text, length = 255) if text diff --git a/app/views/issues/_changesets.rhtml b/app/views/issues/_changesets.rhtml index 52cd60ff571..07660b65d03 100644 --- a/app/views/issues/_changesets.rhtml +++ b/app/views/issues/_changesets.rhtml @@ -1,7 +1,7 @@ <% changesets.each do |changeset| %>
    -

    <%= link_to("#{l(:label_revision)} #{changeset.revision}", - :controller => 'repositories', :action => 'revision', :id => changeset.project, :rev => changeset.revision) %>
    +

    <%= link_to("#{l(:label_revision)} #{changeset_name(changeset)}", + :controller => 'repositories', :action => 'revision', :id => changeset.project, :rev => link_safe_rev(changeset.revision)) %>
    <%= authoring(changeset.committed_on, changeset.author) %>

    <%= textilizable(changeset, :comments) %> diff --git a/app/views/repositories/_dir_list_content.rhtml b/app/views/repositories/_dir_list_content.rhtml index c5bd53ea78b..66574f1c86e 100644 --- a/app/views/repositories/_dir_list_content.rhtml +++ b/app/views/repositories/_dir_list_content.rhtml @@ -17,7 +17,7 @@ <%= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %> <% changeset = @project.repository.changesets.find_by_revision(entry.lastrev.identifier) if entry.lastrev && entry.lastrev.identifier %> -<%= link_to_revision(changeset.revision, @project) if changeset %> +<%= link_to_revision(changeset, @project) if changeset %> <%= distance_of_time_in_words(entry.lastrev.time, Time.now) if entry.lastrev && entry.lastrev.time %> <%= changeset.nil? ? h(entry.lastrev.author.to_s.split('<').first) : changeset.author if entry.lastrev %> <%=h truncate(changeset.comments, :length => 50) unless changeset.nil? %> diff --git a/app/views/repositories/_revisions.rhtml b/app/views/repositories/_revisions.rhtml index 26fb5b6992a..10aac07d6b6 100644 --- a/app/views/repositories/_revisions.rhtml +++ b/app/views/repositories/_revisions.rhtml @@ -13,7 +13,7 @@ <% line_num = 1 %> <% revisions.each do |changeset| %> -<%= link_to_revision(changeset.revision, project) %> +<%= link_to_revision(changeset, project) %> <%= radio_button_tag('rev', changeset.revision, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('cbto-#{line_num+1}').checked=true;") if show_diff && (line_num < revisions.size) %> <%= radio_button_tag('rev_to', changeset.revision, (line_num==2), :id => "cbto-#{line_num}", :onclick => "if ($('cb-#{line_num}').checked==true) {$('cb-#{line_num-1}').checked=true;}") if show_diff && (line_num > 1) %> <%= format_time(changeset.committed_on) %> diff --git a/app/views/repositories/annotate.rhtml b/app/views/repositories/annotate.rhtml index a18e9bbac3e..45c080145e2 100644 --- a/app/views/repositories/annotate.rhtml +++ b/app/views/repositories/annotate.rhtml @@ -19,7 +19,7 @@ <%= line_num %> - <%= (revision.identifier ? link_to(format_revision(revision.identifier), :action => 'revision', :id => @project, :rev => revision.identifier) : format_revision(revision.revision)) if revision %> + <%= (revision.identifier ? link_to(changeset_name(revision), :action => 'revision', :id => @project, :rev => revision.identifier) : changeset_name(revision)) if revision %> <%= h(revision.author.to_s.split('<').first) if revision %>
    <%= line %>
    diff --git a/app/views/repositories/diff.rhtml b/app/views/repositories/diff.rhtml index 24f92a540ae..6795f0d0ffb 100644 --- a/app/views/repositories/diff.rhtml +++ b/app/views/repositories/diff.rhtml @@ -1,4 +1,4 @@ -

    <%= l(:label_revision) %> <%= format_revision(@rev_to) + ':' if @rev_to %><%= format_revision(@rev) %> <%=h @path %>

    +

    <%= l(:label_revision) %> <%= (@end_changeset ? changeset_name(@end_changeset) : format_revision(@rev_to)) + ':' if @rev_to %><%= @start_changeset ? changeset_name(@start_changeset) : format_revision(@rev) %> <%=h @path %>

    <% form_tag({:path => to_path_param(@path)}, :method => 'get') do %> diff --git a/app/views/repositories/revision.rhtml b/app/views/repositories/revision.rhtml index 543dc9e4b6c..7cead2313a1 100644 --- a/app/views/repositories/revision.rhtml +++ b/app/views/repositories/revision.rhtml @@ -1,13 +1,13 @@
    « <% unless @changeset.previous.nil? -%> - <%= link_to_revision(@changeset.previous.revision, @project, :text => l(:label_previous)) %> + <%= link_to_revision(@changeset.previous, @project, :text => l(:label_previous)) %> <% else -%> <%= l(:label_previous) %> <% end -%> | <% unless @changeset.next.nil? -%> - <%= link_to_revision(@changeset.next.revision, @project, :text => l(:label_next)) %> + <%= link_to_revision(@changeset.next, @project, :text => l(:label_next)) %> <% else -%> <%= l(:label_next) %> <% end -%> @@ -19,7 +19,7 @@ <% end %>
    -

    <%= l(:label_revision) %> <%= format_revision(@changeset.revision) %>

    +

    <%= l(:label_revision) %> <%= changeset_name(@changeset) %>

    <% if @changeset.scmid %>ID: <%= @changeset.scmid %>
    <% end %> <%= authoring(@changeset.committed_on, @changeset.author) %>

    diff --git a/db/migrate/20100929174249_add_display_name_to_changesets.rb b/db/migrate/20100929174249_add_display_name_to_changesets.rb new file mode 100644 index 00000000000..8a96d5e0c42 --- /dev/null +++ b/db/migrate/20100929174249_add_display_name_to_changesets.rb @@ -0,0 +1,9 @@ +class AddDisplayNameToChangesets < ActiveRecord::Migration + def self.up + add_column :changesets, :display_name, :string + end + + def self.down + remove_column :changesets, :display_name + end +end diff --git a/lib/redmine/scm/adapters/abstract_adapter.rb b/lib/redmine/scm/adapters/abstract_adapter.rb index a3ca61e2361..ab78080565b 100644 --- a/lib/redmine/scm/adapters/abstract_adapter.rb +++ b/lib/redmine/scm/adapters/abstract_adapter.rb @@ -271,7 +271,7 @@ def latest end class Revision - attr_accessor :identifier, :scmid, :name, :author, :time, :message, :paths, :revision, :branch + attr_accessor :identifier, :scmid, :name, :author, :time, :message, :paths, :revision, :branch, :display_name def initialize(attributes={}) self.identifier = attributes[:identifier] @@ -283,6 +283,7 @@ def initialize(attributes={}) self.paths = attributes[:paths] self.revision = attributes[:revision] self.branch = attributes[:branch] + self.display_name = attributes[:display_name] end def save(repo) @@ -291,6 +292,7 @@ def save(repo) :repository => repo, :revision => identifier, :scmid => scmid, + :display_name => display_name, :committer => author, :committed_on => time, :comments => message) diff --git a/lib/redmine/scm/adapters/bazaar_adapter.rb b/lib/redmine/scm/adapters/bazaar_adapter.rb index 8cd13b0df76..acf22da3eb1 100644 --- a/lib/redmine/scm/adapters/bazaar_adapter.rb +++ b/lib/redmine/scm/adapters/bazaar_adapter.rb @@ -97,8 +97,7 @@ def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}) next unless revision if line =~ /^\s*revno: (\d+(\.\d+)*)($|\s\[merge\]$)/ - #revision.identifier = $1 - # I'll figure out what to do with this later + revision.display_name = $1 elsif line =~ /^\s*committer: (.+)$/ revision.author = $1.strip elsif line =~ /^\s*revision-id:(.+)$/ From 3c88e1803ce5ff163057b96ff42a7ea9e6db8543 Mon Sep 17 00:00:00 2001 From: Brent Sowers Date: Wed, 29 Sep 2010 15:16:53 -0400 Subject: [PATCH 31/60] Added ability to clear all changesets and re-fetch, rake redmine:clear_and_fetch_changesets --- app/models/repository.rb | 7 +++++++ lib/tasks/fetch_changesets.rake | 3 +++ 2 files changed, 10 insertions(+) diff --git a/app/models/repository.rb b/app/models/repository.rb index dee705c97d3..a980ff2d6cb 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -176,6 +176,13 @@ def self.fetch_changesets end end end + + # Wipes out all changesets for this repository and fetches them new. Good for + # VCSes like Bazaar where revision numbers can change. + def self.clear_and_fetch_changesets + clear_changesets + fetch_changesets + end # scan changeset comments to find related and fixed issues for all repositories def self.scan_changesets_for_issue_ids diff --git a/lib/tasks/fetch_changesets.rake b/lib/tasks/fetch_changesets.rake index 681032bd680..6ffb0a5173b 100644 --- a/lib/tasks/fetch_changesets.rake +++ b/lib/tasks/fetch_changesets.rake @@ -21,4 +21,7 @@ namespace :redmine do task :fetch_changesets => :environment do Repository.fetch_changesets end + task :clear_and_fetch_changesets => :environment do + Repository.clear_and_fetch_changesets + end end From e6a925ab415c70b05e65b7d66a26b2991b746709 Mon Sep 17 00:00:00 2001 From: Brent Sowers Date: Wed, 29 Sep 2010 15:27:17 -0400 Subject: [PATCH 32/60] Fixed bug from last commit with clear and fetch changesets --- app/models/repository.rb | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index a980ff2d6cb..979d363012d 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -180,8 +180,12 @@ def self.fetch_changesets # Wipes out all changesets for this repository and fetches them new. Good for # VCSes like Bazaar where revision numbers can change. def self.clear_and_fetch_changesets - clear_changesets - fetch_changesets + Project.active.has_module(:repository).find(:all, :include => :repository).each do |project| + if project.repository + project.repository.clear_changesets + end + end + self.fetch_changesets end # scan changeset comments to find related and fixed issues for all repositories @@ -203,6 +207,14 @@ def self.factory(klass_name, *args) rescue nil end + + def clear_changesets + cs, ch, ci = Changeset.table_name, Change.table_name, "#{table_name_prefix}changesets_issues#{table_name_suffix}" + connection.delete("DELETE FROM #{ch} WHERE #{ch}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})") + connection.delete("DELETE FROM #{ci} WHERE #{ci}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})") + connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}") + end + private @@ -213,10 +225,4 @@ def before_save true end - def clear_changesets - cs, ch, ci = Changeset.table_name, Change.table_name, "#{table_name_prefix}changesets_issues#{table_name_suffix}" - connection.delete("DELETE FROM #{ch} WHERE #{ch}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})") - connection.delete("DELETE FROM #{ci} WHERE #{ci}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})") - connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}") - end end From 1a4fc3b5339458dabac9f4de77b12562d79e1d41 Mon Sep 17 00:00:00 2001 From: Brent Sowers Date: Wed, 29 Sep 2010 15:58:46 -0400 Subject: [PATCH 33/60] More bug fixes for clearing changesets, I'm pretty sure it's working now --- app/models/repository.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index 979d363012d..97660c54d0d 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -181,7 +181,7 @@ def self.fetch_changesets # VCSes like Bazaar where revision numbers can change. def self.clear_and_fetch_changesets Project.active.has_module(:repository).find(:all, :include => :repository).each do |project| - if project.repository + if project.repository && project.repository.respond_to?(:clear_changesets) project.repository.clear_changesets end end From cd746fe00f8c74c9dada8eb786b33b644ef9838b Mon Sep 17 00:00:00 2001 From: Brent Sowers Date: Mon, 11 Oct 2010 21:44:43 -0400 Subject: [PATCH 34/60] Fixed two general repository bugs that were brought about by bazaar changes --- app/controllers/repositories_controller.rb | 2 +- app/helpers/application_helper.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index 3a686d304a8..fc3f70e26f2 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -169,7 +169,7 @@ def diff User.current.preference.save end @start_changeset = @repository.find_changeset_by_name(@rev) - @end_changeset = @respository.find_changeset_by_name(@rev_to) if @rev_to + @end_changeset = @repository.find_changeset_by_name(@rev_to) if @rev_to @cache_key = "repositories/diff/#{@repository.id}/" + Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}") unless read_fragment(@cache_key) @diff = @repository.diff(@path, @rev, @rev_to) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 96c8767f892..1842990b361 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -189,7 +189,7 @@ def format_revision(txt) # when it's part of the URL in a link. Call this on a revision when # including it in a link def link_safe_rev(revision) - revision.gsub(/\./, "_") + revision ? revision.gsub(/\./, "_") : nil end def format_version_name(version) From c6d46fc0f64323076b2f0cd5be78a3fb5dad9deb Mon Sep 17 00:00:00 2001 From: Brent Sowers Date: Mon, 11 Oct 2010 21:53:13 -0400 Subject: [PATCH 35/60] Fixed more repository bugs introduced in bazaar changes. All repository tests other than Bazaar and darcs (which was previously failing) are now passing --- app/controllers/repositories_controller.rb | 2 +- app/helpers/application_helper.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index fc3f70e26f2..741a67cf4ab 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -204,7 +204,7 @@ def find_repository (render_404; return false) unless @repository @path = params[:path].join('/') unless params[:path].nil? || params[:path].empty? @path ||= '' - @rev = params[:rev].blank? ? @repository.default_branch : params[:rev].strip.gsub(/_/, ".") + @rev = params[:rev].blank? ? @repository.default_branch : params[:rev].strip.gsub(/___/, ".") @rev_to = params[:rev_to] rescue ActiveRecord::RecordNotFound render_404 diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 1842990b361..2d7eea39e8c 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -117,7 +117,7 @@ def link_to_attachment(attachment, options={}) # Options: # * :text - Link text (default to the formatted revision) def link_to_revision(revision, project, options={}) - revision_identifier = revision.revision.gsub(/\./, "_") + revision_identifier = revision.revision.gsub(/\./, "___") text = options.delete(:text) || (revision.respond_to?(:display_name) && revision.display_name ? revision.display_name : format_revision(revision.revision)) link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => revision_identifier}, :title => l(:label_revision_id, revision.revision)) @@ -189,7 +189,7 @@ def format_revision(txt) # when it's part of the URL in a link. Call this on a revision when # including it in a link def link_safe_rev(revision) - revision ? revision.gsub(/\./, "_") : nil + revision ? revision.gsub(/\./, "___") : nil end def format_version_name(version) From 62156a399d8fbba554830a402443605085837245 Mon Sep 17 00:00:00 2001 From: Brent Sowers Date: Mon, 11 Oct 2010 22:18:47 -0400 Subject: [PATCH 36/60] Fixed annotate in bazaar, all bazaar unit tests now pass --- lib/redmine/scm/adapters/bazaar_adapter.rb | 8 ++---- test/unit/repository_bazaar_test.rb | 32 +++++++++++----------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/lib/redmine/scm/adapters/bazaar_adapter.rb b/lib/redmine/scm/adapters/bazaar_adapter.rb index acf22da3eb1..892ef72488b 100644 --- a/lib/redmine/scm/adapters/bazaar_adapter.rb +++ b/lib/redmine/scm/adapters/bazaar_adapter.rb @@ -174,16 +174,14 @@ def cat(path, identifier=nil) end def annotate(path, identifier=nil) - cmd = "#{BZR_BIN} annotate --all" + cmd = "#{BZR_BIN} annotate --all --show-ids" cmd << " -r revid:#{identifier}" if identifier cmd << " #{target(path)}" blame = Annotate.new shellout(cmd) do |io| - author = nil - identifier = nil io.each_line do |line| - next unless line =~ %r{^(\d+(\.\d+)*) ([^|]+)\| (.*)$} - blame.add_line($3.rstrip, Revision.new(:identifier => $1, :author => $3.strip)) + next unless line =~ %r{^(.+?)\-(\d+)\-(.+?) \| (.*)$} + blame.add_line($4.rstrip, Revision.new(:identifier => "#{$1}-#{$2}-#{$3}", :author => $1.strip)) end end return nil if $? && $?.exitstatus != 0 diff --git a/test/unit/repository_bazaar_test.rb b/test/unit/repository_bazaar_test.rb index c29e04edeb9..0ea34d5b90e 100644 --- a/test/unit/repository_bazaar_test.rb +++ b/test/unit/repository_bazaar_test.rb @@ -34,20 +34,20 @@ def test_fetch_changesets_from_scratch @repository.fetch_changesets @repository.reload - assert_equal 4, @repository.changesets.count - assert_equal 9, @repository.changes.count - assert_equal 'Initial import', @repository.changesets.find_by_revision('1').comments + assert_equal 8, @repository.changesets.count + assert_equal 13, @repository.changes.count + assert_equal 'Initial commit, just one file', @repository.changesets.find_by_display_name('1').comments end def test_fetch_changesets_incremental @repository.fetch_changesets - # Remove changesets with revision > 5 - @repository.changesets.find(:all).each {|c| c.destroy if c.revision.to_i > 2} + # Remove changesets with revision > 2 + @repository.changesets.find(:all).each {|c| c.destroy if c.display_name.to_i > 2} @repository.reload assert_equal 2, @repository.changesets.count @repository.fetch_changesets - assert_equal 4, @repository.changesets.count + assert_equal 8, @repository.changesets.count end def test_entries @@ -58,28 +58,28 @@ def test_entries assert_equal 'directory', entries[0].name assert_equal 'file', entries[1].kind - assert_equal 'doc-mkdir.txt', entries[1].name + assert_equal 'root_level.txt', entries[1].name end def test_entries_in_subdirectory entries = @repository.entries('directory') - assert_equal 3, entries.size + assert_equal 4, entries.size assert_equal 'file', entries.last.kind - assert_equal 'edit.png', entries.last.name + assert_equal 'source2.txt', entries.last.name end def test_cat - cat = @repository.scm.cat('directory/document.txt') - assert cat =~ /Write the contents of a file as of a given revision to standard output/ + cat = @repository.scm.cat('directory/source2.txt') + assert cat =~ /New source code file/, cat end def test_annotate - annotate = @repository.scm.annotate('doc-mkdir.txt') - assert_equal 17, annotate.lines.size - assert_equal 1, annotate.revisions[0].identifier - assert_equal 'jsmith@', annotate.revisions[0].author - assert_equal 'mkdir', annotate.lines[0] + annotate = @repository.scm.annotate('root_level.txt') + assert_equal 2, annotate.lines.size + assert_equal "second@no.server-20100927143241-aknlenpvde342upv", annotate.revisions[0].identifier + assert_equal 'second@no.server', annotate.revisions[0].author + assert_equal '#First file, not much to say here', annotate.lines[0] end else puts "Bazaar test repository NOT FOUND. Skipping unit tests !!!" From d829950cffe2d093de4fb2da2f758bbeb1185e9b Mon Sep 17 00:00:00 2001 From: Brent Sowers Date: Tue, 12 Oct 2010 22:48:27 -0400 Subject: [PATCH 37/60] Working on getting bazaar repository controller tests to pass, still need to add more functionality to tests --- .../repositories_bazaar_controller_test.rb | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/functional/repositories_bazaar_controller_test.rb b/test/functional/repositories_bazaar_controller_test.rb index 18841d54e47..b4d59efcfe9 100644 --- a/test/functional/repositories_bazaar_controller_test.rb +++ b/test/functional/repositories_bazaar_controller_test.rb @@ -51,7 +51,7 @@ def test_browse_root assert_not_nil assigns(:entries) assert_equal 2, assigns(:entries).size assert assigns(:entries).detect {|e| e.name == 'directory' && e.kind == 'dir'} - assert assigns(:entries).detect {|e| e.name == 'doc-mkdir.txt' && e.kind == 'file'} + assert assigns(:entries).detect {|e| e.name == 'root_level.txt' && e.kind == 'file'} end def test_browse_directory @@ -59,7 +59,7 @@ def test_browse_directory assert_response :success assert_template 'show' assert_not_nil assigns(:entries) - assert_equal ['doc-ls.txt', 'document.txt', 'edit.png'], assigns(:entries).collect(&:name) + assert_equal ["config.txt", "edit.png", "second_file.txt", "source2.txt"], assigns(:entries).collect(&:name) entry = assigns(:entries).detect {|e| e.name == 'edit.png'} assert_not_nil entry assert_equal 'file', entry.kind @@ -67,29 +67,29 @@ def test_browse_directory end def test_browse_at_given_revision - get :show, :id => 3, :path => [], :rev => 3 + get :show, :id => 3, :path => [], :rev => 'johndoe@no.server-20100927142810-5hx3443dk9mdbs3t' assert_response :success assert_template 'show' assert_not_nil assigns(:entries) - assert_equal ['directory', 'doc-deleted.txt', 'doc-ls.txt', 'doc-mkdir.txt'], assigns(:entries).collect(&:name) + assert_equal ['directory', 'mainfile.txt'], assigns(:entries).collect(&:name) end def test_changes - get :changes, :id => 3, :path => ['doc-mkdir.txt'] + get :changes, :id => 3, :path => ['root_level.txt'] assert_response :success assert_template 'changes' - assert_tag :tag => 'h2', :content => 'doc-mkdir.txt' + assert_tag :tag => 'h2', :content => 'root_level.txt' end def test_entry_show - get :entry, :id => 3, :path => ['directory', 'doc-ls.txt'] + get :entry, :id => 3, :path => ['directory', 'root_level.txt'] assert_response :success assert_template 'entry' - # Line 19 + # Line 2 assert_tag :tag => 'th', - :content => /29/, + :content => /2/, :attributes => { :class => /line-num/ }, - :sibling => { :tag => 'td', :content => /Show help message/ } + :sibling => { :tag => 'td', :content => /The above line is incorrect / } end def test_entry_download From 0b1340dce8679fc8156c9cbc9017ca869b5841d4 Mon Sep 17 00:00:00 2001 From: Brent Sowers Date: Wed, 13 Oct 2010 21:37:37 -0400 Subject: [PATCH 38/60] More fixes to the bazaar repository controller test, only one failure left to fix --- .../repositories_bazaar_controller_test.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/functional/repositories_bazaar_controller_test.rb b/test/functional/repositories_bazaar_controller_test.rb index b4d59efcfe9..1117207b624 100644 --- a/test/functional/repositories_bazaar_controller_test.rb +++ b/test/functional/repositories_bazaar_controller_test.rb @@ -82,21 +82,21 @@ def test_changes end def test_entry_show - get :entry, :id => 3, :path => ['directory', 'root_level.txt'] + get :entry, :id => 3, :path => ['directory', 'second_file.txt'] assert_response :success assert_template 'entry' # Line 2 assert_tag :tag => 'th', :content => /2/, :attributes => { :class => /line-num/ }, - :sibling => { :tag => 'td', :content => /The above line is incorrect / } + :sibling => { :tag => 'td', :content => /More code from/ } end def test_entry_download - get :entry, :id => 3, :path => ['directory', 'doc-ls.txt'], :format => 'raw' + get :entry, :id => 3, :path => ['directory', 'second_file.txt'], :format => 'raw' assert_response :success # File content - assert @response.body.include?('Show help message') + assert @response.body.include?('More code from') end def test_directory_entry @@ -109,7 +109,7 @@ def test_directory_entry def test_diff # Full diff of changeset 3 - get :diff, :id => 3, :rev => 3 + get :diff, :id => 3, :rev => 'johndoe@no.server-20100927142810-5hx3443dk9mdbs3t' assert_response :success assert_template 'diff' # Line 22 removed @@ -117,7 +117,7 @@ def test_diff :content => /2/, :sibling => { :tag => 'td', :attributes => { :class => /diff_in/ }, - :content => /Main purpose/ } + :content => /Added another line to the file/ } end def test_annotate From 83e731f647e44e4ebf78684b4c0cf6a2293eb9c8 Mon Sep 17 00:00:00 2001 From: Brent Sowers Date: Sun, 17 Oct 2010 18:27:05 -0400 Subject: [PATCH 39/60] Moved . in revision path fix from helper methods and substitutions in to a requirement in the route --- app/controllers/repositories_controller.rb | 2 +- app/helpers/application_helper.rb | 9 +-------- app/helpers/repositories_helper.rb | 6 +++--- app/views/issues/_changesets.rhtml | 2 +- app/views/repositories/_breadcrumbs.rhtml | 4 ++-- app/views/repositories/revision.rhtml | 2 +- config/routes.rb | 2 +- 7 files changed, 10 insertions(+), 17 deletions(-) diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index 741a67cf4ab..881f7ebe5c9 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -204,7 +204,7 @@ def find_repository (render_404; return false) unless @repository @path = params[:path].join('/') unless params[:path].nil? || params[:path].empty? @path ||= '' - @rev = params[:rev].blank? ? @repository.default_branch : params[:rev].strip.gsub(/___/, ".") + @rev = params[:rev].blank? ? @repository.default_branch : params[:rev].strip @rev_to = params[:rev_to] rescue ActiveRecord::RecordNotFound render_404 diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 2d7eea39e8c..ab866620330 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -117,7 +117,7 @@ def link_to_attachment(attachment, options={}) # Options: # * :text - Link text (default to the formatted revision) def link_to_revision(revision, project, options={}) - revision_identifier = revision.revision.gsub(/\./, "___") + revision_identifier = revision.revision text = options.delete(:text) || (revision.respond_to?(:display_name) && revision.display_name ? revision.display_name : format_revision(revision.revision)) link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => revision_identifier}, :title => l(:label_revision_id, revision.revision)) @@ -185,13 +185,6 @@ def format_revision(txt) txt.to_s[0,8] end - # Some VCSes (Bazaar) have a . in the revision number, which causes problems - # when it's part of the URL in a link. Call this on a revision when - # including it in a link - def link_safe_rev(revision) - revision ? revision.gsub(/\./, "___") : nil - end - def format_version_name(version) if version.project == @project h(version) diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb index 24154a12750..f89bea84924 100644 --- a/app/helpers/repositories_helper.rb +++ b/app/helpers/repositories_helper.rb @@ -84,7 +84,7 @@ def render_changes_tree(tree) :action => 'show', :id => @project, :path => path_param, - :rev => link_safe_rev(@changeset.revision)) + :rev => @changeset.revision) output << "
  • #{text}
  • " output << render_changes_tree(s) elsif c = tree[file][:c] @@ -94,13 +94,13 @@ def render_changes_tree(tree) :action => 'entry', :id => @project, :path => path_param, - :rev => link_safe_rev(@changeset.revision)) unless c.action == 'D' + :rev => @changeset.revision) 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 => link_safe_rev(@changeset.revision)) + ') ' if c.action == 'M' + :rev => @changeset.revision) + ') ' if c.action == 'M' text << ' ' + content_tag('span', c.from_path, :class => 'copied-from') unless c.from_path.blank? output << "
  • #{text}
  • " end diff --git a/app/views/issues/_changesets.rhtml b/app/views/issues/_changesets.rhtml index 07660b65d03..5e2972bdc70 100644 --- a/app/views/issues/_changesets.rhtml +++ b/app/views/issues/_changesets.rhtml @@ -1,7 +1,7 @@ <% changesets.each do |changeset| %>

    <%= link_to("#{l(:label_revision)} #{changeset_name(changeset)}", - :controller => 'repositories', :action => 'revision', :id => changeset.project, :rev => link_safe_rev(changeset.revision)) %>
    + :controller => 'repositories', :action => 'revision', :id => changeset.project, :rev => changeset.revision) %>
    <%= authoring(changeset.committed_on, changeset.author) %>

    <%= textilizable(changeset, :comments) %> diff --git a/app/views/repositories/_breadcrumbs.rhtml b/app/views/repositories/_breadcrumbs.rhtml index 56fbcaabe7a..bab0ff33875 100644 --- a/app/views/repositories/_breadcrumbs.rhtml +++ b/app/views/repositories/_breadcrumbs.rhtml @@ -10,10 +10,10 @@ dirs.each do |dir| link_path << '/' unless link_path.empty? link_path << "#{dir}" %> - / <%= link_to h(dir), :action => 'show', :id => @project, :path => to_path_param(link_path), :rev => link_safe_rev(@rev) %> + / <%= link_to h(dir), :action => 'show', :id => @project, :path => to_path_param(link_path), :rev => @rev %> <% end %> <% if filename %> - / <%= link_to h(filename), :action => 'changes', :id => @project, :path => to_path_param("#{link_path}/#{filename}"), :rev => link_safe_rev(@rev) %> + / <%= link_to h(filename), :action => 'changes', :id => @project, :path => to_path_param("#{link_path}/#{filename}"), :rev => @rev %> <% end %> <%= "@ #{h revision}" if revision %> diff --git a/app/views/repositories/revision.rhtml b/app/views/repositories/revision.rhtml index 7cead2313a1..6098bf26bcb 100644 --- a/app/views/repositories/revision.rhtml +++ b/app/views/repositories/revision.rhtml @@ -45,7 +45,7 @@
  • <%= l(:label_deleted) %>
  • -

    <%= link_to(l(:label_view_diff), :action => 'diff', :id => @project, :path => "", :rev => link_safe_rev(@changeset.revision)) if @changeset.changes.any? %>

    +

    <%= link_to(l(:label_view_diff), :action => 'diff', :id => @project, :path => "", :rev => @changeset.revision) if @changeset.changes.any? %>

    <%= render_changeset_changes %> diff --git a/config/routes.rb b/config/routes.rb index eab2157d8ea..e305fcf5fa4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -208,7 +208,7 @@ repository_views.connect 'projects/:id/repository/statistics', :action => 'stats' repository_views.connect 'projects/:id/repository/revisions', :action => 'revisions' repository_views.connect 'projects/:id/repository/revisions.:format', :action => 'revisions' - repository_views.connect 'projects/:id/repository/revisions/:rev', :action => 'revision' + repository_views.connect 'projects/:id/repository/revisions/:rev', :action => 'revision', :requirements => { :rev => /[a-z0-9\.\-_]+/ } repository_views.connect 'projects/:id/repository/revisions/:rev/diff', :action => 'diff' repository_views.connect 'projects/:id/repository/revisions/:rev/diff.:format', :action => 'diff' repository_views.connect 'projects/:id/repository/revisions/:rev/raw/*path', :action => 'entry', :format => 'raw', :requirements => { :rev => /[a-z0-9\.\-_]+/ } From 107a754807c716cfeb52f9cf4d6894505031f65d Mon Sep 17 00:00:00 2001 From: Brent Sowers Date: Sun, 17 Oct 2010 18:40:08 -0400 Subject: [PATCH 40/60] Fix to last commit for Bazaar revision IDs --- config/routes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/routes.rb b/config/routes.rb index e305fcf5fa4..d7815108545 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -208,7 +208,7 @@ repository_views.connect 'projects/:id/repository/statistics', :action => 'stats' repository_views.connect 'projects/:id/repository/revisions', :action => 'revisions' repository_views.connect 'projects/:id/repository/revisions.:format', :action => 'revisions' - repository_views.connect 'projects/:id/repository/revisions/:rev', :action => 'revision', :requirements => { :rev => /[a-z0-9\.\-_]+/ } + repository_views.connect 'projects/:id/repository/revisions/:rev', :action => 'revision', :requirements => { :rev => /[a-z0-9\.\-_@]+/ } repository_views.connect 'projects/:id/repository/revisions/:rev/diff', :action => 'diff' repository_views.connect 'projects/:id/repository/revisions/:rev/diff.:format', :action => 'diff' repository_views.connect 'projects/:id/repository/revisions/:rev/raw/*path', :action => 'entry', :format => 'raw', :requirements => { :rev => /[a-z0-9\.\-_]+/ } From 4282ef039e62e0abfd325f6d7ddde0228d193148 Mon Sep 17 00:00:00 2001 From: Brent Sowers Date: Sun, 17 Oct 2010 18:50:19 -0400 Subject: [PATCH 41/60] Bazaar repository controller test now passes. Still need to add more bazaar tests --- test/functional/repositories_bazaar_controller_test.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/functional/repositories_bazaar_controller_test.rb b/test/functional/repositories_bazaar_controller_test.rb index 1117207b624..65f2e590009 100644 --- a/test/functional/repositories_bazaar_controller_test.rb +++ b/test/functional/repositories_bazaar_controller_test.rb @@ -121,14 +121,14 @@ def test_diff end def test_annotate - get :annotate, :id => 3, :path => ['doc-mkdir.txt'] + get :annotate, :id => 3, :path => ['root_level.txt'] assert_response :success assert_template 'annotate' # Line 2, revision 3 assert_tag :tag => 'th', :content => /2/, - :sibling => { :tag => 'td', :child => { :tag => 'a', :content => /3/ } }, - :sibling => { :tag => 'td', :content => /jsmith/ }, - :sibling => { :tag => 'td', :content => /Main purpose/ } + :sibling => { :tag => 'td', :child => { :tag => 'a', :content => /second@no\.server\-20100927143241\-aknlenpvde342upv/ } }, + :sibling => { :tag => 'td', :content => /second@no\.server/ }, + :sibling => { :tag => 'td', :content => /The above line is incorrect/ } end else puts "Bazaar test repository NOT FOUND. Skipping functional tests !!!" From ee9e1ac1f41bb0bb77dd2ae499cd06d3ce847434 Mon Sep 17 00:00:00 2001 From: Brent Sowers Date: Sun, 17 Oct 2010 22:46:22 -0400 Subject: [PATCH 42/60] Removed some unused code from Bazaar adapter, started adding unit tests for Bazaar adapter --- app/models/repository/bazaar.rb | 40 +------------------ lib/redmine/scm/adapters/bazaar_adapter.rb | 2 +- .../scm/adapters/bazaar_adapter_test.rb | 36 +++++++++++++++++ 3 files changed, 39 insertions(+), 39 deletions(-) create mode 100644 test/unit/lib/redmine/scm/adapters/bazaar_adapter_test.rb diff --git a/app/models/repository/bazaar.rb b/app/models/repository/bazaar.rb index 10c5ab83699..dd37726817a 100644 --- a/app/models/repository/bazaar.rb +++ b/app/models/repository/bazaar.rb @@ -51,43 +51,6 @@ def entries(path=nil, identifier=nil) end end 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 : 0 -# # latest revision in the repository -# scm_revision = scm_info.lastrev.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 200 -# identifier_to = [identifier_from + 199, scm_revision].min -# revisions = scm.revisions('', identifier_to, identifier_from, :with_paths => true) -# transaction do -# revisions.reverse_each do |revision| -# changeset = Changeset.create(:repository => self, -# :revision => revision.identifier, -# :committer => revision.author, -# :committed_on => revision.time, -# :scmid => revision.scmid, -# :comments => revision.message) -# -# revision.paths.each do |change| -# Change.create(:changeset => changeset, -# :action => change[:action], -# :path => change[:path], -# :revision => change[:revision]) -# end -# end -# end unless revisions.nil? -# identifier_from = identifier_to + 1 -# end -# end -# end -# end # With SCM's that have a sequential commit numbering, redmine is able to be # clever and only fetch changesets going forward from the most recent one @@ -96,7 +59,8 @@ def entries(path=nil, identifier=nil) # the entire log. Since it's way too slow for large repositories, we only # parse 1 week before the last known commit. # The repository can still be fully reloaded by calling #clear_changesets - # before fetching changesets (eg. for offline resync) + # before fetching changesets (eg. for offline resync), you can set this up + # as an external job with rake redmine:clear_and_fetch_changesets def fetch_changesets c = changesets.find(:first, :order => 'committed_on DESC') since = (c ? c.committed_on - 7.days : nil) diff --git a/lib/redmine/scm/adapters/bazaar_adapter.rb b/lib/redmine/scm/adapters/bazaar_adapter.rb index 892ef72488b..2fdefb25677 100644 --- a/lib/redmine/scm/adapters/bazaar_adapter.rb +++ b/lib/redmine/scm/adapters/bazaar_adapter.rb @@ -33,7 +33,7 @@ def info if io.read =~ %r{^(\d+(\.\d+)*)\r?$} info = Info.new({:root_url => url, :lastrev => Revision.new({ - :identifier => $1 + :display_name => $1 }) }) end diff --git a/test/unit/lib/redmine/scm/adapters/bazaar_adapter_test.rb b/test/unit/lib/redmine/scm/adapters/bazaar_adapter_test.rb new file mode 100644 index 00000000000..4ee4746a391 --- /dev/null +++ b/test/unit/lib/redmine/scm/adapters/bazaar_adapter_test.rb @@ -0,0 +1,36 @@ +require File.dirname(__FILE__) + '/../../../../../test_helper' + +class BazaarAdapterTest < ActiveSupport::TestCase + REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/bazaar_repository' + + if File.directory?(REPOSITORY_PATH) + def setup + @adapter = Redmine::Scm::Adapters::BazaarAdapter.new(REPOSITORY_PATH) + end + + def test_info + info = @adapter.info + assert_equal "7", info.lastrev.display_name + end + + def test_entries + current_entries = @adapter.entries + assert_equal 2, current_entries.length + assert_equal "root_level.txt", current_entries[1].name + + old_entries = @adapter.entries("", "johndoe@no.server-20100927142810-5hx3443dk9mdbs3t") + assert_equal 2, old_entries.length + assert_equal "mainfile.txt", old_entries[1].name + + entries_dir = @adapter.entries("directory") + assert_equal 4, entries_dir.length + assert_equal "config.txt", entries_dir[0].name + end + + # NOT DONE YET!!! + + else + puts "Bazaar test repository NOT FOUND. Skipping unit tests !!!" + def test_fake; assert true end + end +end From fd247826332cb358dc81dbb8ddb855d7c6f81f1e Mon Sep 17 00:00:00 2001 From: Brent Sowers Date: Tue, 26 Oct 2010 21:18:42 -0400 Subject: [PATCH 43/60] Completed Bazaar adapter unit test --- lib/redmine/scm/adapters/bazaar_adapter.rb | 2 +- .../scm/adapters/bazaar_adapter_test.rb | 78 ++++++++++++++++++- test/unit/repository_bazaar_test.rb | 12 --- 3 files changed, 78 insertions(+), 14 deletions(-) diff --git a/lib/redmine/scm/adapters/bazaar_adapter.rb b/lib/redmine/scm/adapters/bazaar_adapter.rb index 2fdefb25677..36bb0572d0d 100644 --- a/lib/redmine/scm/adapters/bazaar_adapter.rb +++ b/lib/redmine/scm/adapters/bazaar_adapter.rb @@ -77,7 +77,7 @@ def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}) revisions = Revisions.new cmd = "#{BZR_BIN} log -n 0 -v --show-ids" if options[:since] - cmd << " -r date:#{shell_quote(options[:since].strftime("%Y-%m-%d,%H:%M:%S"))}.." + cmd << " -r date:#{options[:since].strftime("%Y-%m-%d,%H:%M:%S")}.." else from = identifier_from ? "revid:#{identifier_from}" : "last:1" to = identifier_to ? "revid:#{identifier_to}" : "1" diff --git a/test/unit/lib/redmine/scm/adapters/bazaar_adapter_test.rb b/test/unit/lib/redmine/scm/adapters/bazaar_adapter_test.rb index 4ee4746a391..e93d6a4fa3b 100644 --- a/test/unit/lib/redmine/scm/adapters/bazaar_adapter_test.rb +++ b/test/unit/lib/redmine/scm/adapters/bazaar_adapter_test.rb @@ -27,7 +27,83 @@ def test_entries assert_equal "config.txt", entries_dir[0].name end - # NOT DONE YET!!! + def test_revisions_no_options + revs = @adapter.revisions + assert_equal 8, revs.length + last_rev = revs[0] + sub_rev = revs[1] + assert_equal "7", last_rev.display_name + assert_equal "second@no.server-20100927143627-e2mreqlpaodcixpg", last_rev.identifier + assert_equal "Second Developer ", last_rev.author + assert_equal Time.gm(2010,9,27,14,36,27).utc, last_rev.time.utc + assert_equal "4.1.1", sub_rev.display_name + assert_equal "johndoe@no.server-20100927143451-vw9ij1q1max8nakq", sub_rev.identifier + assert_equal "John Doe ", sub_rev.author + assert_equal Time.gm(2010,9,27,14,34,51).utc, sub_rev.time.utc + [last_rev, sub_rev].each do |r| + assert_equal 1, r.paths.length + assert_equal "/directory/config.txt", r.paths[0][:path] + assert_equal "A", r.paths[0][:action] + assert_equal "config.txt-20100927143445-xgkt26w4b98wdc15-1", r.paths[0][:revision] + end + end + + def test_revision_path + revs = @adapter.revisions("directory/second_file.txt") + assert_equal 2, revs.length + assert_equal "6", revs[0].display_name + assert_equal "5", revs[-1].display_name + assert_equal 2, revs[0].paths.length + end + + def test_revisions_identifers + revs = @adapter.revisions(nil, "second@no.server-20100927143409-waety1q0cm1ur3sv", "johndoe@no.server-20100927142845-un2x20a6r2t3nz1w") + assert_equal 3, revs.length + assert_equal "6", revs[0].display_name + assert_equal "4", revs[-1].display_name + + revs = @adapter.revisions(nil, "second@no.server-20100927143409-waety1q0cm1ur3sv") + assert_equal 6, revs.length + assert_equal "6", revs[0].display_name + assert_equal "1", revs[-1].display_name + + revs = @adapter.revisions(nil, nil, "johndoe@no.server-20100927142845-un2x20a6r2t3nz1w") + assert_equal 5, revs.length + assert_equal "7", revs[0].display_name + assert_equal "4", revs[-1].display_name + end + + def test_revisions_options + revs = @adapter.revisions(nil, nil, nil, {:since => Time.gm(2010,9,27,14,34,0).localtime}) + assert_equal 3, revs.length + assert_equal "7", revs[0].display_name + assert_equal "6", revs[-1].display_name + end + + def test_diff + diff = @adapter.diff(nil, "second@no.server-20100927143627-e2mreqlpaodcixpg") + assert_equal 6, diff.length + assert_equal "+This is a placeholder for configuration data\n", diff[4] + + diff = @adapter.diff(nil, "johndoe@no.server-20100927142810-5hx3443dk9mdbs3t", "johndoe@no.server-20100927142357-09lh9svlopfrt2zh") + assert_equal 10, diff.length + assert_equal "+This file is in the directory\n", diff[7] + end + + def test_cat + assert_equal "#First file, not much to say here\nThe above line is incorrect\n", @adapter.cat("root_level.txt") + assert_equal "First file, not much to say here\n", @adapter.cat("root_level.txt", "johndoe@no.server-20100927142845-un2x20a6r2t3nz1w") + end + + def test_annotate + an = @adapter.annotate("directory/second_file.txt") + assert_equal 2, an.lines.length + assert_equal "second@no.server-20100927143241-aknlenpvde342upv", an.revisions[0].identifier + assert_equal 'second@no.server', an.revisions[0].author + assert_equal "This file was created by second developer", an.lines[0] + assert_equal "second@no.server-20100927143409-waety1q0cm1ur3sv", an.revisions[1].identifier + assert_equal "More code from", an.lines[1] + end else puts "Bazaar test repository NOT FOUND. Skipping unit tests !!!" diff --git a/test/unit/repository_bazaar_test.rb b/test/unit/repository_bazaar_test.rb index 0ea34d5b90e..846a0b66e8d 100644 --- a/test/unit/repository_bazaar_test.rb +++ b/test/unit/repository_bazaar_test.rb @@ -69,18 +69,6 @@ def test_entries_in_subdirectory assert_equal 'source2.txt', entries.last.name end - def test_cat - cat = @repository.scm.cat('directory/source2.txt') - assert cat =~ /New source code file/, cat - end - - def test_annotate - annotate = @repository.scm.annotate('root_level.txt') - assert_equal 2, annotate.lines.size - assert_equal "second@no.server-20100927143241-aknlenpvde342upv", annotate.revisions[0].identifier - assert_equal 'second@no.server', annotate.revisions[0].author - assert_equal '#First file, not much to say here', annotate.lines[0] - end else puts "Bazaar test repository NOT FOUND. Skipping unit tests !!!" def test_fake; assert true end From 749622d24f41e0fbf1cbf9c3a153444eea40b155 Mon Sep 17 00:00:00 2001 From: Toshi MARUYAMA Date: Wed, 27 Oct 2010 15:05:35 +0900 Subject: [PATCH 44/60] fix bazaar adapter revisions(). --- lib/redmine/scm/adapters/bazaar_adapter.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/redmine/scm/adapters/bazaar_adapter.rb b/lib/redmine/scm/adapters/bazaar_adapter.rb index 36bb0572d0d..8cbc43aa08b 100644 --- a/lib/redmine/scm/adapters/bazaar_adapter.rb +++ b/lib/redmine/scm/adapters/bazaar_adapter.rb @@ -97,12 +97,12 @@ def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}) next unless revision if line =~ /^\s*revno: (\d+(\.\d+)*)($|\s\[merge\]$)/ - revision.display_name = $1 + revision.revision = $1 elsif line =~ /^\s*committer: (.+)$/ revision.author = $1.strip elsif line =~ /^\s*revision-id:(.+)$/ revision.scmid = $1.strip - revision.identifier = revision.scmid + revision.identifier = revision.revision elsif line =~ /^\s*timestamp: (.+)$/ revision.time = Time.parse($1).localtime elsif line =~ /^ \s*-----/ From 45b240f1a5b2de6ed5b74d66c40c6dbf18c76522 Mon Sep 17 00:00:00 2001 From: Toshi MARUYAMA Date: Wed, 27 Oct 2010 15:37:02 +0900 Subject: [PATCH 45/60] fix app bazaar. --- app/models/repository/bazaar.rb | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/app/models/repository/bazaar.rb b/app/models/repository/bazaar.rb index dd37726817a..9e9e2740830 100644 --- a/app/models/repository/bazaar.rb +++ b/app/models/repository/bazaar.rb @@ -28,7 +28,17 @@ def scm_adapter def self.scm_name 'Bazaar' end - + + # Returns the identifier for the given mercurial changeset + def self.changeset_identifier(changeset) + changeset.scmid + end + + # Returns the readable identifier for the given mercurial changeset + def self.format_changeset_identifier(changeset) + changeset.revision + end + def entries(path=nil, identifier=nil) entries = scm.entries(path, identifier) if entries @@ -81,6 +91,17 @@ def fetch_changesets revisions.each{|r| r.save(self)} unless revisions.nil? end + # Finds and returns a revision with a number or the beginning of a hash + def find_changeset_by_name(name) + if /[^\d\.]/ =~ name + e = changesets.find(:first, :conditions => ['scmid = ?', name.to_s]) + else + e = changesets.find(:first, :conditions => ['revision = ?', name.to_s]) + end + return e if e + changesets.find(:first, :conditions => ['scmid LIKE ?', "#{name}%"]) # last ditch + end + def latest_changesets(path,rev,limit=10) revisions = scm.revisions(path, nil, rev, :limit => limit, :all => false) return [] if revisions.nil? || revisions.empty? From 88d1b5e7a5186cf3800cc018ad084fc907206b0f Mon Sep 17 00:00:00 2001 From: Toshi MARUYAMA Date: Wed, 27 Oct 2010 15:49:46 +0900 Subject: [PATCH 46/60] fix bazaar adapter info. --- lib/redmine/scm/adapters/bazaar_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/redmine/scm/adapters/bazaar_adapter.rb b/lib/redmine/scm/adapters/bazaar_adapter.rb index 8cbc43aa08b..60ac2a3e88a 100644 --- a/lib/redmine/scm/adapters/bazaar_adapter.rb +++ b/lib/redmine/scm/adapters/bazaar_adapter.rb @@ -33,7 +33,7 @@ def info if io.read =~ %r{^(\d+(\.\d+)*)\r?$} info = Info.new({:root_url => url, :lastrev => Revision.new({ - :display_name => $1 + :identifier => $1 }) }) end From 87534ce5e506399607da3e4535fdc4b9c0d862a1 Mon Sep 17 00:00:00 2001 From: brentsowers1 Date: Wed, 27 Oct 2010 13:22:08 -0400 Subject: [PATCH 47/60] Bazaar diff controller action bug fix --- config/routes.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/routes.rb b/config/routes.rb index b668258d138..a1b0cf005de 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -202,8 +202,8 @@ repository_views.connect 'projects/:id/repository/revisions', :action => 'revisions' repository_views.connect 'projects/:id/repository/revisions.:format', :action => 'revisions' repository_views.connect 'projects/:id/repository/revisions/:rev', :action => 'revision', :requirements => { :rev => /[a-z0-9\.\-_@]+/ } - repository_views.connect 'projects/:id/repository/revisions/:rev/diff', :action => 'diff' - repository_views.connect 'projects/:id/repository/revisions/:rev/diff.:format', :action => 'diff' + repository_views.connect 'projects/:id/repository/revisions/:rev/diff', :action => 'diff', :requirements => { :rev => /[a-z0-9\.\-_@]+/ } + repository_views.connect 'projects/:id/repository/revisions/:rev/diff.:format', :action => 'diff', :requirements => { :rev => /[a-z0-9\.\-_@]+/ } repository_views.connect 'projects/:id/repository/revisions/:rev/raw/*path', :action => 'entry', :format => 'raw', :requirements => { :rev => /[a-z0-9\.\-_]+/ } repository_views.connect 'projects/:id/repository/revisions/:rev/:action/*path', :requirements => { :rev => /[a-z0-9\.\-_]+/ } repository_views.connect 'projects/:id/repository/raw/*path', :action => 'entry', :format => 'raw' From 41888cb62c48b9e45587d4936513c3aff91b5f84 Mon Sep 17 00:00:00 2001 From: Brent Sowers Date: Thu, 28 Oct 2010 18:41:47 -0400 Subject: [PATCH 48/60] Updated Bazaar tests to work with latest changes from Mercurial changes merge --- app/models/repository/bazaar.rb | 2 +- lib/redmine/scm/adapters/bazaar_adapter.rb | 3 +- .../scm/adapters/bazaar_adapter_test.rb | 30 +++++++++---------- test/unit/repository_bazaar_test.rb | 6 ++-- 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/app/models/repository/bazaar.rb b/app/models/repository/bazaar.rb index 9e9e2740830..fd4972e1022 100644 --- a/app/models/repository/bazaar.rb +++ b/app/models/repository/bazaar.rb @@ -80,7 +80,7 @@ def fetch_changesets recent_changesets = changesets.find(:all, :conditions => ['committed_on >= ?', since]) - # Clean out revisions that are no longer in git + # Clean out revisions that are no longer in bazaar recent_changesets.each {|c| c.destroy unless revisions.detect {|r| r.scmid.to_s == c.scmid.to_s }} # Subtract revisions that redmine already knows about diff --git a/lib/redmine/scm/adapters/bazaar_adapter.rb b/lib/redmine/scm/adapters/bazaar_adapter.rb index 60ac2a3e88a..cb8828b151d 100644 --- a/lib/redmine/scm/adapters/bazaar_adapter.rb +++ b/lib/redmine/scm/adapters/bazaar_adapter.rb @@ -33,7 +33,8 @@ def info if io.read =~ %r{^(\d+(\.\d+)*)\r?$} info = Info.new({:root_url => url, :lastrev => Revision.new({ - :identifier => $1 + :identifier => $1, + :revision => $1 }) }) end diff --git a/test/unit/lib/redmine/scm/adapters/bazaar_adapter_test.rb b/test/unit/lib/redmine/scm/adapters/bazaar_adapter_test.rb index e93d6a4fa3b..381f22b870f 100644 --- a/test/unit/lib/redmine/scm/adapters/bazaar_adapter_test.rb +++ b/test/unit/lib/redmine/scm/adapters/bazaar_adapter_test.rb @@ -10,7 +10,7 @@ def setup def test_info info = @adapter.info - assert_equal "7", info.lastrev.display_name + assert_equal "7", info.lastrev.revision end def test_entries @@ -32,12 +32,12 @@ def test_revisions_no_options assert_equal 8, revs.length last_rev = revs[0] sub_rev = revs[1] - assert_equal "7", last_rev.display_name - assert_equal "second@no.server-20100927143627-e2mreqlpaodcixpg", last_rev.identifier + assert_equal "7", last_rev.revision + assert_equal "second@no.server-20100927143627-e2mreqlpaodcixpg", last_rev.scmid assert_equal "Second Developer ", last_rev.author assert_equal Time.gm(2010,9,27,14,36,27).utc, last_rev.time.utc - assert_equal "4.1.1", sub_rev.display_name - assert_equal "johndoe@no.server-20100927143451-vw9ij1q1max8nakq", sub_rev.identifier + assert_equal "4.1.1", sub_rev.revision + assert_equal "johndoe@no.server-20100927143451-vw9ij1q1max8nakq", sub_rev.scmid assert_equal "John Doe ", sub_rev.author assert_equal Time.gm(2010,9,27,14,34,51).utc, sub_rev.time.utc [last_rev, sub_rev].each do |r| @@ -51,33 +51,33 @@ def test_revisions_no_options def test_revision_path revs = @adapter.revisions("directory/second_file.txt") assert_equal 2, revs.length - assert_equal "6", revs[0].display_name - assert_equal "5", revs[-1].display_name + assert_equal "6", revs[0].revision + assert_equal "5", revs[-1].revision assert_equal 2, revs[0].paths.length end def test_revisions_identifers revs = @adapter.revisions(nil, "second@no.server-20100927143409-waety1q0cm1ur3sv", "johndoe@no.server-20100927142845-un2x20a6r2t3nz1w") assert_equal 3, revs.length - assert_equal "6", revs[0].display_name - assert_equal "4", revs[-1].display_name + assert_equal "6", revs[0].revision + assert_equal "4", revs[-1].revision revs = @adapter.revisions(nil, "second@no.server-20100927143409-waety1q0cm1ur3sv") assert_equal 6, revs.length - assert_equal "6", revs[0].display_name - assert_equal "1", revs[-1].display_name + assert_equal "6", revs[0].revision + assert_equal "1", revs[-1].revision revs = @adapter.revisions(nil, nil, "johndoe@no.server-20100927142845-un2x20a6r2t3nz1w") assert_equal 5, revs.length - assert_equal "7", revs[0].display_name - assert_equal "4", revs[-1].display_name + assert_equal "7", revs[0].revision + assert_equal "4", revs[-1].revision end def test_revisions_options revs = @adapter.revisions(nil, nil, nil, {:since => Time.gm(2010,9,27,14,34,0).localtime}) assert_equal 3, revs.length - assert_equal "7", revs[0].display_name - assert_equal "6", revs[-1].display_name + assert_equal "7", revs[0].revision + assert_equal "6", revs[-1].revision end def test_diff diff --git a/test/unit/repository_bazaar_test.rb b/test/unit/repository_bazaar_test.rb index 846a0b66e8d..4f382248389 100644 --- a/test/unit/repository_bazaar_test.rb +++ b/test/unit/repository_bazaar_test.rb @@ -36,13 +36,15 @@ def test_fetch_changesets_from_scratch assert_equal 8, @repository.changesets.count assert_equal 13, @repository.changes.count - assert_equal 'Initial commit, just one file', @repository.changesets.find_by_display_name('1').comments + assert_equal 'Initial commit, just one file', @repository.find_changeset_by_name('1').comments + assert_equal 'Initial commit, just one file', + @repository.find_changeset_by_name('johndoe@no.server-20100927142357-09lh9svlopfrt2zh').comments end def test_fetch_changesets_incremental @repository.fetch_changesets # Remove changesets with revision > 2 - @repository.changesets.find(:all).each {|c| c.destroy if c.display_name.to_i > 2} + @repository.changesets.find(:all).each {|c| c.destroy if c.revision.to_i > 2} @repository.reload assert_equal 2, @repository.changesets.count From 6f71f088b7bf6daea48a8545755f36d5263a0d91 Mon Sep 17 00:00:00 2001 From: Toshi MARUYAMA Date: Fri, 29 Oct 2010 09:38:12 +0900 Subject: [PATCH 49/60] Fix app/models/repository/bazaar.rb comments --- app/models/repository/bazaar.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/repository/bazaar.rb b/app/models/repository/bazaar.rb index 9e9e2740830..50b7f7bf008 100644 --- a/app/models/repository/bazaar.rb +++ b/app/models/repository/bazaar.rb @@ -29,12 +29,12 @@ def self.scm_name 'Bazaar' end - # Returns the identifier for the given mercurial changeset + # Returns the identifier for the given bazaar changeset def self.changeset_identifier(changeset) changeset.scmid end - # Returns the readable identifier for the given mercurial changeset + # Returns the readable identifier for the given bazaar changeset def self.format_changeset_identifier(changeset) changeset.revision end From bb5bd0814eb0ce8ff44dd73d08257dce0df8cbd1 Mon Sep 17 00:00:00 2001 From: Toshi MARUYAMA Date: Fri, 29 Oct 2010 10:06:33 +0900 Subject: [PATCH 50/60] Bazaar diff controller action bug fix --- config/routes.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/routes.rb b/config/routes.rb index e55c41d49bd..1e58698a35d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -204,8 +204,8 @@ repository_views.connect 'projects/:id/repository/revisions/:rev', :action => 'revision', :requirements => { :rev => /[a-z0-9\.\-_@]+/ } repository_views.connect 'projects/:id/repository/revisions/:rev/diff', :action => 'diff', :requirements => { :rev => /[a-z0-9\.\-_@]+/ } repository_views.connect 'projects/:id/repository/revisions/:rev/diff.:format', :action => 'diff', :requirements => { :rev => /[a-z0-9\.\-_@]+/ } - repository_views.connect 'projects/:id/repository/revisions/:rev/raw/*path', :action => 'entry', :format => 'raw', :requirements => { :rev => /[a-z0-9\.\-_]+/ } - repository_views.connect 'projects/:id/repository/revisions/:rev/:action/*path', :requirements => { :rev => /[a-z0-9\.\-_]+/ } + repository_views.connect 'projects/:id/repository/revisions/:rev/raw/*path', :action => 'entry', :format => 'raw', :requirements => { :rev => /[a-z0-9\.\-_@]+/ } + repository_views.connect 'projects/:id/repository/revisions/:rev/:action/*path', :requirements => { :rev => /[a-z0-9\.\-_@]+/ } repository_views.connect 'projects/:id/repository/raw/*path', :action => 'entry', :format => 'raw' # TODO: why the following route is required? repository_views.connect 'projects/:id/repository/entry/*path', :action => 'entry' From 1ddc97700016ce93bba2c6b2222dbf37d82f501f Mon Sep 17 00:00:00 2001 From: Brent Sowers Date: Fri, 29 Oct 2010 16:05:52 -0400 Subject: [PATCH 51/60] Fixed bug in revision display on diff and annotate for Bazaar --- app/views/repositories/diff.rhtml | 2 +- lib/redmine/scm/adapters/bazaar_adapter.rb | 28 ++++++++++++++++++- .../repositories_bazaar_controller_test.rb | 2 +- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/app/views/repositories/diff.rhtml b/app/views/repositories/diff.rhtml index 24f92a540ae..9590071f622 100644 --- a/app/views/repositories/diff.rhtml +++ b/app/views/repositories/diff.rhtml @@ -1,4 +1,4 @@ -

    <%= l(:label_revision) %> <%= format_revision(@rev_to) + ':' if @rev_to %><%= format_revision(@rev) %> <%=h @path %>

    +

    <%= l(:label_revision) %> <%= format_revision(@end_changeset) + ':' if @end_changeset %><%= format_revision(@start_changeset) %> <%=h @path %>

    <% form_tag({:path => to_path_param(@path)}, :method => 'get') do %> diff --git a/lib/redmine/scm/adapters/bazaar_adapter.rb b/lib/redmine/scm/adapters/bazaar_adapter.rb index cb8828b151d..c2793ca7216 100644 --- a/lib/redmine/scm/adapters/bazaar_adapter.rb +++ b/lib/redmine/scm/adapters/bazaar_adapter.rb @@ -21,6 +21,13 @@ module Redmine module Scm module Adapters class BazaarAdapter < AbstractAdapter + + class Revision < Redmine::Scm::Adapters::Revision + # Returns the readable identifier + def format_identifier + revision + end + end # Bazaar executable name BZR_BIN = "bzr" @@ -179,12 +186,31 @@ def annotate(path, identifier=nil) cmd << " -r revid:#{identifier}" if identifier cmd << " #{target(path)}" blame = Annotate.new + # With Bazaar, there is no way to show both the regular revision + # number (ie 121, 4.1.1, etc.) and the internal ID with one command. + # So run through the command twice + lines = [] shellout(cmd) do |io| io.each_line do |line| next unless line =~ %r{^(.+?)\-(\d+)\-(.+?) \| (.*)$} - blame.add_line($4.rstrip, Revision.new(:identifier => "#{$1}-#{$2}-#{$3}", :author => $1.strip)) + lines << {:text => $4.rstrip, :scmid => "#{$1}-#{$2}-#{$3}", :author => $1.strip} end end + cmd.gsub!(/ \-\-show\-ids/, "") + i = 0 + shellout(cmd) do |io| + io.each_line do |line| + next unless line =~ %r{^([\d\.]+) .*? \| (.*)$} + lines[i][:revision] = $1 + i += 1 + end + end + + lines.each do |l| + blame.add_line(l[:text], Revision.new(:identifier => l[:scmid], + :author => l[:author], :scmid => l[:scmid], + :revision => l[:revision])) + end return nil if $? && $?.exitstatus != 0 blame end diff --git a/test/functional/repositories_bazaar_controller_test.rb b/test/functional/repositories_bazaar_controller_test.rb index 65f2e590009..9c6ca56e636 100644 --- a/test/functional/repositories_bazaar_controller_test.rb +++ b/test/functional/repositories_bazaar_controller_test.rb @@ -126,7 +126,7 @@ def test_annotate assert_template 'annotate' # Line 2, revision 3 assert_tag :tag => 'th', :content => /2/, - :sibling => { :tag => 'td', :child => { :tag => 'a', :content => /second@no\.server\-20100927143241\-aknlenpvde342upv/ } }, + :sibling => { :tag => 'td', :content => /5/, :child => { :tag => 'a', :content => /second@no\.server\-20100927143241\-aknlenpvde342upv/ } }, :sibling => { :tag => 'td', :content => /second@no\.server/ }, :sibling => { :tag => 'td', :content => /The above line is incorrect/ } end From 5a4f62912dd4fedbc295679fb05eff5a48ecf2a1 Mon Sep 17 00:00:00 2001 From: Brent Sowers Date: Fri, 29 Oct 2010 16:10:59 -0400 Subject: [PATCH 52/60] Added Rubymine folder to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 853230d740e..9cb77a40494 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ /tmp/test/* /vendor/rails *.rbc +/.idea From 77da045a5519899257a92ac984306d34a958fe0a Mon Sep 17 00:00:00 2001 From: Brent Sowers Date: Sun, 31 Oct 2010 12:32:52 -0400 Subject: [PATCH 53/60] Fixed bug in annotate revision links --- lib/redmine/scm/adapters/bazaar_adapter.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/redmine/scm/adapters/bazaar_adapter.rb b/lib/redmine/scm/adapters/bazaar_adapter.rb index c2793ca7216..95c208fea28 100644 --- a/lib/redmine/scm/adapters/bazaar_adapter.rb +++ b/lib/redmine/scm/adapters/bazaar_adapter.rb @@ -192,7 +192,7 @@ def annotate(path, identifier=nil) lines = [] shellout(cmd) do |io| io.each_line do |line| - next unless line =~ %r{^(.+?)\-(\d+)\-(.+?) \| (.*)$} + next unless line =~ %r{^\s*(\S+?)\-(\d+)\-(.+?) \| (.*)$} lines << {:text => $4.rstrip, :scmid => "#{$1}-#{$2}-#{$3}", :author => $1.strip} end end @@ -200,7 +200,7 @@ def annotate(path, identifier=nil) i = 0 shellout(cmd) do |io| io.each_line do |line| - next unless line =~ %r{^([\d\.]+) .*? \| (.*)$} + next unless line =~ %r{^\s*([\d\.]+) .*? \| (.*)$} lines[i][:revision] = $1 i += 1 end From e8dd8c42cb3af01a55562c7d5849a3949a3c0a3d Mon Sep 17 00:00:00 2001 From: Brent Sowers Date: Sun, 31 Oct 2010 12:35:38 -0400 Subject: [PATCH 54/60] Added pyc files (for mercurial redminehelper) to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9cb77a40494..61c1e709091 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ /vendor/rails *.rbc /.idea +*.pyc From 1125855cf5c0cffbcb9df530c5f91df6b2c59f2e Mon Sep 17 00:00:00 2001 From: Toshi MARUYAMA Date: Mon, 1 Nov 2010 13:05:35 +0900 Subject: [PATCH 55/60] Bazaar diff revision for human readable --- app/controllers/repositories_controller.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index b6dcc317343..ae2f488265b 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -149,7 +149,7 @@ def revision rescue ChangesetNotFound show_error_not_found end - + def diff if params[:format] == 'diff' @diff = @repository.diff(@path, @rev, @rev_to) @@ -168,15 +168,20 @@ def diff User.current.pref[:diff_type] = @diff_type User.current.preference.save end - + @cache_key = "repositories/diff/#{@repository.id}/" + Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}") unless read_fragment(@cache_key) @diff = @repository.diff(@path, @rev, @rev_to) show_error_not_found unless @diff end + + # Mercurial and Bazaar @rev are commit id + @start_changeset = @repository.find_changeset_by_name(@rev) + @end_changeset = @repository.find_changeset_by_name(@rev_to) if @rev_to + end end - + def stats end From d26bda6c8b7a6ada8a0e7d68bfbdb0d801fd9376 Mon Sep 17 00:00:00 2001 From: Toshi MARUYAMA Date: Mon, 1 Nov 2010 13:06:58 +0900 Subject: [PATCH 56/60] Change diff 'rev1:rev2' to 'rev1 rev2'. Mercurial diff 'rev1:scmid1:rev2:scmid2' is confused. --- app/views/repositories/diff.rhtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/repositories/diff.rhtml b/app/views/repositories/diff.rhtml index 9590071f622..c5b8df28c92 100644 --- a/app/views/repositories/diff.rhtml +++ b/app/views/repositories/diff.rhtml @@ -1,4 +1,4 @@ -

    <%= l(:label_revision) %> <%= format_revision(@end_changeset) + ':' if @end_changeset %><%= format_revision(@start_changeset) %> <%=h @path %>

    +

    <%= l(:label_revision) %> <%= format_revision(@end_changeset) + ' ' if @end_changeset %><%= format_revision(@start_changeset) %> <%=h @path %>

    <% form_tag({:path => to_path_param(@path)}, :method => 'get') do %> From b4e0f3becdd801ace34e1fcc98f9af60cbd8bb58 Mon Sep 17 00:00:00 2001 From: Toshi MARUYAMA Date: Mon, 1 Nov 2010 13:11:47 +0900 Subject: [PATCH 57/60] use format_revision() for html title. --- app/views/repositories/revision.rhtml | 2 +- app/views/repositories/revisions.rhtml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/repositories/revision.rhtml b/app/views/repositories/revision.rhtml index 1f3f611a123..483e358de39 100644 --- a/app/views/repositories/revision.rhtml +++ b/app/views/repositories/revision.rhtml @@ -56,4 +56,4 @@ <%= stylesheet_link_tag "scm" %> <% end %> -<% html_title("#{l(:label_revision)} #{@changeset.revision}") -%> +<% html_title("#{l(:label_revision)} #{format_revision(@changeset)}") -%> diff --git a/app/views/repositories/revisions.rhtml b/app/views/repositories/revisions.rhtml index 255cb6221f4..b645c66eb0a 100644 --- a/app/views/repositories/revisions.rhtml +++ b/app/views/repositories/revisions.rhtml @@ -20,4 +20,4 @@ <%= f.link_to 'Atom', :url => {:key => User.current.rss_key} %> <% end %> -<% html_title(l(:label_revision_plural)) -%> +<% html_title("#{l(:label_revision)} #{@changeset.revision}") -%> From 710d3f355de44d87ccb3ec0caf49ec77917ca16b Mon Sep 17 00:00:00 2001 From: Toshi MARUYAMA Date: Mon, 13 Dec 2010 23:10:39 +0900 Subject: [PATCH 58/60] Truncate Git revision labels in Activity page/feed http://www.redmine.org/issues/6092 --- app/controllers/repositories_controller.rb | 3 ++ app/helpers/application_helper.rb | 4 +- app/helpers/repositories_helper.rb | 14 +++-- app/models/changeset.rb | 22 +++++++- app/models/repository/git.rb | 10 ++++ .../repositories/_dir_list_content.rhtml | 2 +- app/views/repositories/_revisions.rhtml | 6 +-- app/views/repositories/annotate.rhtml | 2 +- app/views/repositories/diff.rhtml | 2 +- app/views/repositories/revision.rhtml | 10 ++-- lib/redmine/scm/adapters/abstract_adapter.rb | 13 ++++- lib/redmine/scm/adapters/git_adapter.rb | 7 +++ test/unit/changeset_test.rb | 5 ++ test/unit/repository_bazaar_test.rb | 2 +- test/unit/repository_git_test.rb | 26 ++++++++++ test/unit/repository_subversion_test.rb | 51 +++++++++++++++++++ 16 files changed, 158 insertions(+), 21 deletions(-) diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index b6dcc317343..66286071a32 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -174,6 +174,9 @@ def diff @diff = @repository.diff(@path, @rev, @rev_to) show_error_not_found unless @diff end + + @changeset = @repository.find_changeset_by_name(@rev) + @changeset_to = @rev_to ? @repository.find_changeset_by_name(@rev_to) : nil end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 8d4f90c7600..2313ef72455 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -104,8 +104,10 @@ def link_to_attachment(attachment, options={}) # * :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 diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb index 95962873541..62af7e6abd7 100644 --- a/app/helpers/repositories_helper.rb +++ b/app/helpers/repositories_helper.rb @@ -18,8 +18,12 @@ require 'iconv' module RepositoriesHelper - def format_revision(txt) - txt.to_s[0,8] + def format_revision(revision) + if revision.respond_to? :format_identifier + revision.format_identifier + else + revision.to_s + end end def truncate_at_line_break(text, length = 255) @@ -87,7 +91,7 @@ def render_changes_tree(tree) :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 +101,13 @@ def render_changes_tree(tree) :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 diff --git a/app/models/changeset.rb b/app/models/changeset.rb index 804719481e2..dfd8191a015 100644 --- a/app/models/changeset.rb +++ b/app/models/changeset.rb @@ -23,10 +23,10 @@ class Changeset < ActiveRecord::Base 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)} #{o.format_identifier}" + (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,15 @@ class Changeset < ActiveRecord::Base def revision=(r) write_attribute :revision, (r.nil? ? nil : r.to_s) end + + # Returns the identifier of this changeset; depending on repository backends + def identifier + if repository.class.respond_to? :changeset_identifier + repository.class.changeset_identifier self + else + revision.to_s + end + end def comments=(comment) write_attribute(:comments, Changeset.normalize_comments(comment)) @@ -56,6 +65,15 @@ def committed_on=(date) self.commit_date = date super end + + # Returns the readable identifier + def format_identifier + if repository.class.respond_to? :format_changeset_identifier + repository.class.format_changeset_identifier self + else + identifier + end + end def committer=(arg) write_attribute(:committer, self.class.to_utf8(arg.to_s)) diff --git a/app/models/repository/git.rb b/app/models/repository/git.rb index 473eb07e290..9349f3c113a 100644 --- a/app/models/repository/git.rb +++ b/app/models/repository/git.rb @@ -29,6 +29,16 @@ def self.scm_name 'Git' end + # Returns the identifier for the given git changeset + def self.changeset_identifier(changeset) + changeset.scmid + end + + # Returns the readable identifier for the given git changeset + def self.format_changeset_identifier(changeset) + changeset.revision[0, 8] + end + def branches scm.branches end diff --git a/app/views/repositories/_dir_list_content.rhtml b/app/views/repositories/_dir_list_content.rhtml index c5bd53ea78b..66574f1c86e 100644 --- a/app/views/repositories/_dir_list_content.rhtml +++ b/app/views/repositories/_dir_list_content.rhtml @@ -17,7 +17,7 @@ <%= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %> <% changeset = @project.repository.changesets.find_by_revision(entry.lastrev.identifier) if entry.lastrev && entry.lastrev.identifier %> -<%= link_to_revision(changeset.revision, @project) if changeset %> +<%= link_to_revision(changeset, @project) if changeset %> <%= distance_of_time_in_words(entry.lastrev.time, Time.now) if entry.lastrev && entry.lastrev.time %> <%= changeset.nil? ? h(entry.lastrev.author.to_s.split('<').first) : changeset.author if entry.lastrev %> <%=h truncate(changeset.comments, :length => 50) unless changeset.nil? %> diff --git a/app/views/repositories/_revisions.rhtml b/app/views/repositories/_revisions.rhtml index 26fb5b6992a..92c6fb535a7 100644 --- a/app/views/repositories/_revisions.rhtml +++ b/app/views/repositories/_revisions.rhtml @@ -13,9 +13,9 @@ <% line_num = 1 %> <% revisions.each do |changeset| %> -<%= link_to_revision(changeset.revision, project) %> -<%= radio_button_tag('rev', changeset.revision, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('cbto-#{line_num+1}').checked=true;") if show_diff && (line_num < revisions.size) %> -<%= radio_button_tag('rev_to', changeset.revision, (line_num==2), :id => "cbto-#{line_num}", :onclick => "if ($('cb-#{line_num}').checked==true) {$('cb-#{line_num-1}').checked=true;}") if show_diff && (line_num > 1) %> +<%= link_to_revision(changeset, project) %> +<%= radio_button_tag('rev', changeset.identifier, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('cbto-#{line_num+1}').checked=true;") if show_diff && (line_num < revisions.size) %> +<%= radio_button_tag('rev_to', changeset.identifier, (line_num==2), :id => "cbto-#{line_num}", :onclick => "if ($('cb-#{line_num}').checked==true) {$('cb-#{line_num-1}').checked=true;}") if show_diff && (line_num > 1) %> <%= format_time(changeset.committed_on) %> <%=h changeset.author %> <%= textilizable(truncate_at_line_break(changeset.comments)) %> diff --git a/app/views/repositories/annotate.rhtml b/app/views/repositories/annotate.rhtml index a18e9bbac3e..498507148de 100644 --- a/app/views/repositories/annotate.rhtml +++ b/app/views/repositories/annotate.rhtml @@ -19,7 +19,7 @@ <%= line_num %> - <%= (revision.identifier ? link_to(format_revision(revision.identifier), :action => 'revision', :id => @project, :rev => revision.identifier) : format_revision(revision.revision)) if revision %> + <%= (revision.identifier ? link_to_revision(revision, @project) : format_revision(revision)) if revision %> <%= h(revision.author.to_s.split('<').first) if revision %>
    <%= line %>
    diff --git a/app/views/repositories/diff.rhtml b/app/views/repositories/diff.rhtml index 24f92a540ae..525ecd10a7e 100644 --- a/app/views/repositories/diff.rhtml +++ b/app/views/repositories/diff.rhtml @@ -1,4 +1,4 @@ -

    <%= l(:label_revision) %> <%= format_revision(@rev_to) + ':' if @rev_to %><%= format_revision(@rev) %> <%=h @path %>

    +

    <%= l(:label_revision) %> <%= format_revision(@changeset_to) + ' ' if @changeset_to %><%= format_revision(@changeset) %> <%=h @path %>

    <% form_tag({:path => to_path_param(@path)}, :method => 'get') do %> diff --git a/app/views/repositories/revision.rhtml b/app/views/repositories/revision.rhtml index 92597dff7c9..5cf40b1a451 100644 --- a/app/views/repositories/revision.rhtml +++ b/app/views/repositories/revision.rhtml @@ -1,13 +1,13 @@
    « <% unless @changeset.previous.nil? -%> - <%= link_to_revision(@changeset.previous.revision, @project, :text => l(:label_previous)) %> + <%= link_to_revision(@changeset.previous, @project, :text => l(:label_previous)) %> <% else -%> <%= l(:label_previous) %> <% end -%> | <% unless @changeset.next.nil? -%> - <%= link_to_revision(@changeset.next.revision, @project, :text => l(:label_next)) %> + <%= link_to_revision(@changeset.next, @project, :text => l(:label_next)) %> <% else -%> <%= l(:label_next) %> <% end -%> @@ -19,7 +19,7 @@ <% end %>
    -

    <%= l(:label_revision) %> <%= format_revision(@changeset.revision) %>

    +

    <%= l(:label_revision) %> <%= format_revision(@changeset) %>

    <% if @changeset.scmid %>ID: <%= @changeset.scmid %>
    <% end %> <%= authoring(@changeset.committed_on, @changeset.author) %>

    @@ -45,7 +45,7 @@
  • <%= l(:label_deleted) %>
  • -

    <%= link_to(l(:label_view_diff), :action => 'diff', :id => @project, :path => "", :rev => @changeset.revision) if @changeset.changes.any? %>

    +

    <%= link_to(l(:label_view_diff), :action => 'diff', :id => @project, :path => "", :rev => @changeset.identifier) if @changeset.changes.any? %>

    <%= render_changeset_changes %> @@ -56,4 +56,4 @@ <%= stylesheet_link_tag "scm" %> <% end %> -<% html_title("#{l(:label_revision)} #{@changeset.revision}") -%> +<% html_title("#{l(:label_revision)} #{format_revision(@changeset)}") -%> diff --git a/lib/redmine/scm/adapters/abstract_adapter.rb b/lib/redmine/scm/adapters/abstract_adapter.rb index a3ca61e2361..f98e90958f9 100644 --- a/lib/redmine/scm/adapters/abstract_adapter.rb +++ b/lib/redmine/scm/adapters/abstract_adapter.rb @@ -271,7 +271,8 @@ def latest end class Revision - attr_accessor :identifier, :scmid, :name, :author, :time, :message, :paths, :revision, :branch + attr_accessor :scmid, :name, :author, :time, :message, :paths, :revision, :branch + attr_writer :identifier def initialize(attributes={}) self.identifier = attributes[:identifier] @@ -285,6 +286,16 @@ def initialize(attributes={}) self.branch = attributes[:branch] end + # Returns the identifier of this revision; see also Changeset model + def identifier + (@identifier || revision).to_s + end + + # Returns the readable identifier. + def format_identifier + identifier + end + def save(repo) Changeset.transaction do changeset = Changeset.new( diff --git a/lib/redmine/scm/adapters/git_adapter.rb b/lib/redmine/scm/adapters/git_adapter.rb index e801f22f7b6..c02d566a5c0 100644 --- a/lib/redmine/scm/adapters/git_adapter.rb +++ b/lib/redmine/scm/adapters/git_adapter.rb @@ -264,6 +264,13 @@ def cat(path, identifier=nil) return nil if $? && $?.exitstatus != 0 cat end + + class Revision < Redmine::Scm::Adapters::Revision + # Returns the readable identifier + def format_identifier + identifier[0,8] + end + end end end end diff --git a/test/unit/changeset_test.rb b/test/unit/changeset_test.rb index 9265fe9c5f0..2f0415d3ab6 100644 --- a/test/unit/changeset_test.rb +++ b/test/unit/changeset_test.rb @@ -218,4 +218,9 @@ def test_invalid_utf8_sequences_in_comments_should_be_stripped c.comments = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt") assert_equal "Texte encod en ISO-8859-1.", c.comments end + + def test_identifier + c = Changeset.find_by_revision('1') + assert_equal c.revision, c.identifier + end end diff --git a/test/unit/repository_bazaar_test.rb b/test/unit/repository_bazaar_test.rb index bd1c9a9b420..5cd7da13c21 100644 --- a/test/unit/repository_bazaar_test.rb +++ b/test/unit/repository_bazaar_test.rb @@ -77,7 +77,7 @@ def test_cat def test_annotate annotate = @repository.scm.annotate('doc-mkdir.txt') assert_equal 17, annotate.lines.size - assert_equal 1, annotate.revisions[0].identifier + assert_equal '1', annotate.revisions[0].identifier assert_equal 'jsmith@', annotate.revisions[0].author assert_equal 'mkdir', annotate.lines[0] end diff --git a/test/unit/repository_git_test.rb b/test/unit/repository_git_test.rb index acf4f174a86..621d0256510 100644 --- a/test/unit/repository_git_test.rb +++ b/test/unit/repository_git_test.rb @@ -62,6 +62,32 @@ def test_fetch_changesets_incremental @repository.fetch_changesets assert_equal 15, @repository.changesets.count end + + def test_identifier + @repository.fetch_changesets + @repository.reload + c = @repository.changesets.find_by_revision('7234cb2750b63f47bff735edc50a1c0a433c2518') + assert_equal c.scmid, c.identifier + end + + def test_format_identifier + @repository.fetch_changesets + @repository.reload + c = @repository.changesets.find_by_revision('7234cb2750b63f47bff735edc50a1c0a433c2518') + assert_equal c.format_identifier, '7234cb27' + end + + def test_activities + @repository.fetch_changesets + @repository.reload + f = Redmine::Activity::Fetcher.new(User.anonymous, :project => Project.find(1)) + f.scope = ['changesets'] + events = f.events + assert_kind_of Array, events + eve = events[-1] + assert eve.event_title.include?('7234cb27:') + assert_equal eve.event_url[:rev], '7234cb2750b63f47bff735edc50a1c0a433c2518' + end else puts "Git test repository NOT FOUND. Skipping unit tests !!!" def test_fake; assert true end diff --git a/test/unit/repository_subversion_test.rb b/test/unit/repository_subversion_test.rb index 903cdd049c4..cda6d5bdbe9 100644 --- a/test/unit/repository_subversion_test.rb +++ b/test/unit/repository_subversion_test.rb @@ -88,6 +88,57 @@ def test_directory_listing_with_square_brackets_in_base assert_equal 1, entries.size, 'Expect a single entry' assert_equal 'README.txt', entries.first.name end + + def test_identifier + @repository.fetch_changesets + @repository.reload + c = @repository.changesets.find_by_revision('1') + assert_equal c.revision, c.identifier + end + + def test_identifier_nine_digit + c = Changeset.new(:repository => @repository, :committed_on => Time.now, + :revision => '123456789', :comments => 'test') + assert_equal c.identifier, c.revision + end + + def test_format_identifier + @repository.fetch_changesets + @repository.reload + c = @repository.changesets.find_by_revision('1') + assert_equal c.format_identifier, c.revision + end + + def test_format_identifier_nine_digit + c = Changeset.new(:repository => @repository, :committed_on => Time.now, + :revision => '123456789', :comments => 'test') + assert_equal c.format_identifier, c.revision + end + + def test_activities + @repository.fetch_changesets + @repository.reload + f = Redmine::Activity::Fetcher.new(User.anonymous, :project => Project.find(1)) + f.scope = ['changesets'] + events = f.events + assert_kind_of Array, events + eve = events[-1] + assert eve.event_title.include?('1:') + assert_equal eve.event_url[:rev], '1' + end + + def test_activities_nine_digit + c = Changeset.new(:repository => @repository, :committed_on => Time.now, + :revision => '123456789', :comments => 'test') + assert( c.save ) + f = Redmine::Activity::Fetcher.new(User.anonymous, :project => Project.find(1)) + f.scope = ['changesets'] + events = f.events + assert_kind_of Array, events + eve = events[-1] + assert eve.event_title.include?('123456789:') + assert_equal eve.event_url[:rev], '123456789' + end else puts "Subversion test repository NOT FOUND. Skipping unit tests !!!" def test_fake; assert true end From b5440d188a12f829a2472ec8c274f7290579b806 Mon Sep 17 00:00:00 2001 From: Brent Sowers Date: Thu, 16 Dec 2010 18:31:02 -0500 Subject: [PATCH 59/60] Fixed bug when viewing all revisions --- app/views/repositories/revisions.rhtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/repositories/revisions.rhtml b/app/views/repositories/revisions.rhtml index b645c66eb0a..255cb6221f4 100644 --- a/app/views/repositories/revisions.rhtml +++ b/app/views/repositories/revisions.rhtml @@ -20,4 +20,4 @@ <%= f.link_to 'Atom', :url => {:key => User.current.rss_key} %> <% end %> -<% html_title("#{l(:label_revision)} #{@changeset.revision}") -%> +<% html_title(l(:label_revision_plural)) -%> From aec38653e45ffad85865f35ab594e214299f4205 Mon Sep 17 00:00:00 2001 From: Brent Sowers Date: Thu, 16 Dec 2010 21:54:31 -0500 Subject: [PATCH 60/60] Added : in between revision numbers when viewing diff for clarification --- app/views/repositories/diff.rhtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/repositories/diff.rhtml b/app/views/repositories/diff.rhtml index 525ecd10a7e..efe17f17769 100644 --- a/app/views/repositories/diff.rhtml +++ b/app/views/repositories/diff.rhtml @@ -1,4 +1,4 @@ -

    <%= l(:label_revision) %> <%= format_revision(@changeset_to) + ' ' if @changeset_to %><%= format_revision(@changeset) %> <%=h @path %>

    +

    <%= l(:label_revision) %> <%= format_revision(@changeset_to) + ' : ' if @changeset_to %><%= format_revision(@changeset) %> <%=h @path %>

    <% form_tag({:path => to_path_param(@path)}, :method => 'get') do %>