diff app/models/repository/git.rb @ 441:cbce1fd3b1b7 redmine-1.2

Update to Redmine 1.2-stable branch (Redmine SVN rev 6000)
author Chris Cannam
date Mon, 06 Jun 2011 14:24:13 +0100
parents 051f544170fe
children 753f1380d6bc 0c939c159af4
line wrap: on
line diff
--- a/app/models/repository/git.rb	Thu Mar 03 11:42:28 2011 +0000
+++ b/app/models/repository/git.rb	Mon Jun 06 14:24:13 2011 +0100
@@ -1,16 +1,17 @@
-# redMine - project management software
-# Copyright (C) 2006-2007  Jean-Philippe Lang
+# Redmine - project management software
+# Copyright (C) 2006-2011  Jean-Philippe Lang
 # Copyright (C) 2007  Patrick Aljord patcito@ŋmail.com
+#
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
 # as published by the Free Software Foundation; either version 2
 # of the License, or (at your option) any later version.
-# 
+#
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
-# 
+#
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
@@ -21,11 +22,12 @@
   attr_protected :root_url
   validates_presence_of :url
 
-  ATTRIBUTE_KEY_NAMES = {
-      "url"          => "Path to repository",
-    }
   def self.human_attribute_name(attribute_key_name)
-    ATTRIBUTE_KEY_NAMES[attribute_key_name] || super
+    attr_name = attribute_key_name
+    if attr_name == "url"
+      attr_name = "path_to_repository"
+    end
+    super(attr_name)
   end
 
   def self.scm_adapter_class
@@ -36,6 +38,21 @@
     'Git'
   end
 
+  def report_last_commit
+    extra_report_last_commit
+  end
+
+  def extra_report_last_commit
+    return false if extra_info.nil?
+    v = extra_info["extra_report_last_commit"]
+    return false if v.nil?
+    v.to_s != '0'
+  end
+
+  def supports_directory_revisions?
+    true
+  end
+
   def repo_log_encoding
     'UTF-8'
   end
@@ -65,63 +82,92 @@
     changesets.find(:first, :conditions => ['scmid LIKE ?', "#{name}%"])
   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 git, 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.
+  def entries(path=nil, identifier=nil)
+    scm.entries(path,
+                identifier,
+                options = {:report_last_commit => extra_report_last_commit})
+  end
+
+  # In Git and Mercurial, revisions are not in date order.
+  # Redmine Mercurial fixed issues.
+  #    * Redmine Takes Too Long On Large Mercurial Repository
+  #      http://www.redmine.org/issues/3449
+  #    * Sorting for changesets might go wrong on Mercurial repos
+  #      http://www.redmine.org/issues/3567
+  #
+  # Database revision column is text, so Redmine can not sort by revision.
+  # Mercurial has revision number, and revision number guarantees revision order.
+  # Redmine Mercurial model stored revisions ordered by database id to database.
+  # So, Redmine Mercurial model can use correct ordering revisions.
+  #
+  # Redmine Mercurial adapter uses "hg log -r 0:tip --limit 10"
+  # to get limited revisions from old to new.
+  # But, Git 1.7.3.4 does not support --reverse with -n or --skip.
+  #
   # The repository can still be fully reloaded by calling #clear_changesets
   # before fetching changesets (eg. for offline resync)
   def fetch_changesets
-    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
-    unless revisions.nil?
-      revisions.each do |rev|
+    scm_brs = branches
+    return if scm_brs.nil? || scm_brs.empty?
+    h1 = extra_info || {}
+    h  = h1.dup
+    h["branches"]       ||= {}
+    h["db_consistent"]  ||= {}
+    if changesets.count == 0
+      h["db_consistent"]["ordering"] = 1
+      merge_extra_info(h)
+      self.save
+    elsif ! h["db_consistent"].has_key?("ordering")
+      h["db_consistent"]["ordering"] = 0
+      merge_extra_info(h)
+      self.save
+    end
+    scm_brs.each do |br|
+      from_scmid = nil
+      from_scmid = h["branches"][br]["last_scmid"] if h["branches"][br]
+      h["branches"][br] ||= {}
+      scm.revisions('', from_scmid, br, {:reverse => true}) do |rev|
+        db_rev = find_changeset_by_name(rev.revision)
         transaction do
-          changeset = Changeset.new(
-              :repository => self,
-              :revision   => rev.identifier,
-              :scmid      => rev.scmid,
-              :committer  => rev.author, 
-              :committed_on => rev.time,
-              :comments   => rev.message)
-            
-          if changeset.save
-            rev.paths.each do |file|
-              Change.create(
-                  :changeset => changeset,
-                  :action    => file[:action],
-                  :path      => file[:path])
-            end
+          if db_rev.nil?
+            save_revision(rev)
           end
+          h["branches"][br]["last_scmid"] = rev.scmid
+          merge_extra_info(h)
+          self.save
         end
       end
     end
   end
 
+  def save_revision(rev)
+    changeset = Changeset.new(
+              :repository   => self,
+              :revision     => rev.identifier,
+              :scmid        => rev.scmid,
+              :committer    => rev.author,
+              :committed_on => rev.time,
+              :comments     => rev.message
+              )
+    if changeset.save
+      rev.paths.each do |file|
+        Change.create(
+                  :changeset => changeset,
+                  :action    => file[:action],
+                  :path      => file[:path])
+      end
+    end
+  end
+  private :save_revision
+
   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, 
+      :all,
       :conditions => [
-        "scmid IN (?)", 
+        "scmid IN (?)",
         revisions.map!{|c| c.scmid}
       ],
       :order => 'committed_on DESC'