diff --git a/app/models/changeset.rb b/app/models/changeset.rb index f714563..fcd7bb1 100644 --- a/app/models/changeset.rb +++ b/app/models/changeset.rb @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2008 Jean-Philippe Lang +# Copyright (C) 2006-2010 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -57,6 +57,10 @@ class Changeset < ActiveRecord::Base super end + def committer=(arg) + write_attribute(:committer, self.class.to_utf8(arg.to_s)) + end + def project repository.project end @@ -171,11 +175,12 @@ class Changeset < ActiveRecord::Base encoding = Setting.commit_logs_encoding.to_s.strip unless encoding.blank? || encoding == 'UTF-8' begin - return Iconv.conv('UTF-8', encoding, str) + str = Iconv.conv('UTF-8', encoding, str) rescue Iconv::Failure # do nothing here end end - str + # removes invalid UTF8 sequences + Iconv.conv('UTF-8//IGNORE', 'UTF-8', str + ' ')[0..-3] end end diff --git a/app/models/repository/git.rb b/app/models/repository/git.rb index dd5e9e3..473eb07 100644 --- a/app/models/repository/git.rb +++ b/app/models/repository/git.rb @@ -40,23 +40,26 @@ class Repository::Git < Repository # 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 git, you never know if people have merged - # commits into the middle of the repository history, so we always have to - # parse the entire log. + # 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 - # Save ourselves an expensive operation if we're already up to date - return if scm.num_revisions == changesets.count + c = changesets.find(:first, :order => 'committed_on DESC') + since = (c ? c.committed_on - 7.days : nil) - revisions = scm.revisions('', nil, nil, :all => true) + revisions = scm.revisions('', nil, nil, :all => true, :since => since) return if revisions.nil? || revisions.empty? - # Find revisions that redmine knows about already - existing_revisions = changesets.find(:all).map!{|c| c.scmid} + recent_changesets = changesets.find(:all, :conditions => ['committed_on >= ?', since]) # Clean out revisions that are no longer in git - Changeset.delete_all(["scmid NOT IN (?) AND repository_id = (?)", revisions.map{|r| r.scmid}, self.id]) + 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 - revisions.reject!{|r| existing_revisions.include?(r.scmid)} + 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? diff --git a/db/migrate/20100221100219_add_index_on_changesets_scmid.rb b/db/migrate/20100221100219_add_index_on_changesets_scmid.rb new file mode 100644 index 0000000..96d85a3 --- /dev/null +++ b/db/migrate/20100221100219_add_index_on_changesets_scmid.rb @@ -0,0 +1,9 @@ +class AddIndexOnChangesetsScmid < ActiveRecord::Migration + def self.up + add_index :changesets, [:repository_id, :scmid], :name => :changesets_repos_scmid + end + + def self.down + remove_index :changesets, :name => :changesets_repos_scmid + end +end diff --git a/lib/redmine/scm/adapters/git_adapter.rb b/lib/redmine/scm/adapters/git_adapter.rb index 14e1674..75a33a0 100644 --- a/lib/redmine/scm/adapters/git_adapter.rb +++ b/lib/redmine/scm/adapters/git_adapter.rb @@ -33,21 +33,22 @@ module Redmine end def branches - branches = [] + return @branches if @branches + @branches = [] cmd = "#{GIT_BIN} --git-dir #{target('')} branch" shellout(cmd) do |io| io.each_line do |line| - branches << line.match('\s*\*?\s*(.*)$')[1] + @branches << line.match('\s*\*?\s*(.*)$')[1] end end - branches.sort! + @branches.sort! end def tags - tags = [] + return @tags if @tags cmd = "#{GIT_BIN} --git-dir #{target('')} tag" shellout(cmd) do |io| - io.readlines.sort!.map{|t| t.strip} + @tags = io.readlines.sort!.map{|t| t.strip} end end @@ -110,20 +111,16 @@ module Redmine end end - def num_revisions - cmd = "#{GIT_BIN} --git-dir #{target('')} log --all --pretty=format:'' | wc -l" - shellout(cmd) {|io| io.gets.chomp.to_i + 1} - end - def revisions(path, identifier_from, identifier_to, options={}) revisions = Revisions.new - cmd = "#{GIT_BIN} --git-dir #{target('')} log --find-copies-harder --raw --date=iso --pretty=fuller" + cmd = "#{GIT_BIN} --git-dir #{target('')} log --raw --date=iso --pretty=fuller" cmd << " --reverse" if options[:reverse] cmd << " --all" if options[:all] cmd << " -n #{options[:limit]} " if options[:limit] cmd << " #{shell_quote(identifier_from + '..')} " if identifier_from cmd << " #{shell_quote identifier_to} " if identifier_to + cmd << " --since=#{shell_quote(options[:since].strftime("%Y-%m-%d %H:%M:%S"))}" if options[:since] cmd << " -- #{path}" if path && !path.empty? shellout(cmd) do |io| diff --git a/test/fixtures/encoding/iso-8859-1.txt b/test/fixtures/encoding/iso-8859-1.txt new file mode 100644 index 0000000..8ad6cc0 --- /dev/null +++ b/test/fixtures/encoding/iso-8859-1.txt @@ -0,0 +1 @@ +Texte encod� en ISO-8859-1. \ No newline at end of file diff --git a/test/unit/changeset_test.rb b/test/unit/changeset_test.rb index 8d57c43..8010383 100644 --- a/test/unit/changeset_test.rb +++ b/test/unit/changeset_test.rb @@ -1,5 +1,7 @@ -# redMine - project management software -# Copyright (C) 2006-2007 Jean-Philippe Lang +# encoding: utf-8 +# +# Redmine - project management software +# Copyright (C) 2006-2010 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -117,4 +119,18 @@ class ChangesetTest < ActiveSupport::TestCase changeset = Changeset.find_by_revision('10') assert_nil changeset.next end + + def test_comments_should_be_converted_to_utf8 + with_settings :commit_logs_encoding => 'ISO-8859-1' do + c = Changeset.new + c.comments = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt") + assert_equal "Texte encodé en ISO-8859-1.", c.comments + end + end + + def test_invalid_utf8_sequences_in_comments_should_be_stripped + c = Changeset.new + c.comments = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt") + assert_equal "Texte encod en ISO-8859-1.", c.comments + end end diff --git a/test/unit/repository_git_test.rb b/test/unit/repository_git_test.rb index 5ebbb00..71b3e4d 100644 --- a/test/unit/repository_git_test.rb +++ b/test/unit/repository_git_test.rb @@ -35,7 +35,7 @@ class RepositoryGitTest < ActiveSupport::TestCase @repository.reload assert_equal 12, @repository.changesets.count - assert_equal 20, @repository.changes.count + assert_equal 21, @repository.changes.count commit = @repository.changesets.find(:first, :order => 'committed_on ASC') assert_equal "Initial import.\nThe repository contains 3 files.", commit.comments