You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							160 lines
						
					
					
						
							5.3 KiB
						
					
					
				
			
		
		
	
	
							160 lines
						
					
					
						
							5.3 KiB
						
					
					
				| # Redmine - project management software
 | |
| # Copyright (C) 2006-2013  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
 | |
| # 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.
 | |
| 
 | |
| 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
 | |
| 
 | |
|   # number of changesets to fetch at once
 | |
|   FETCH_AT_ONCE = 100
 | |
| 
 | |
|   def self.human_attribute_name(attribute_key_name, *args)
 | |
|     attr_name = attribute_key_name.to_s
 | |
|     if attr_name == "url"
 | |
|       attr_name = "path_to_repository"
 | |
|     end
 | |
|     super(attr_name, *args)
 | |
|   end
 | |
| 
 | |
|   def self.scm_adapter_class
 | |
|     Redmine::Scm::Adapters::MercurialAdapter
 | |
|   end
 | |
| 
 | |
|   def self.scm_name
 | |
|     'Mercurial'
 | |
|   end
 | |
| 
 | |
|   def supports_directory_revisions?
 | |
|     true
 | |
|   end
 | |
| 
 | |
|   def supports_revision_graph?
 | |
|     true
 | |
|   end
 | |
| 
 | |
|   def repo_log_encoding
 | |
|     'UTF-8'
 | |
|   end
 | |
| 
 | |
|   # Returns the readable identifier for the given mercurial changeset
 | |
|   def self.format_changeset_identifier(changeset)
 | |
|     "#{changeset.revision}:#{changeset.scmid}"
 | |
|   end
 | |
| 
 | |
|   # Returns the identifier for the given Mercurial changeset
 | |
|   def self.changeset_identifier(changeset)
 | |
|     changeset.scmid
 | |
|   end
 | |
| 
 | |
|   def diff_format_revisions(cs, cs_to, sep=':')
 | |
|     super(cs, cs_to, ' ')
 | |
|   end
 | |
| 
 | |
|   # Finds and returns a revision with a number or the beginning of a hash
 | |
|   def find_changeset_by_name(name)
 | |
|     return nil if name.blank?
 | |
|     s = name.to_s
 | |
|     if /[^\d]/ =~ s or s.size > 8
 | |
|       cs = changesets.where(:scmid => s).first
 | |
|     else
 | |
|       cs = changesets.where(:revision => s).first
 | |
|     end
 | |
|     return cs if cs
 | |
|     changesets.where('scmid LIKE ?', "#{s}%").first
 | |
|   end
 | |
| 
 | |
|   # Returns the latest changesets for +path+; sorted by revision number
 | |
|   #
 | |
|   # Because :order => 'id DESC' is defined at 'has_many',
 | |
|   # there is no need to set 'order'.
 | |
|   # But, MySQL test fails.
 | |
|   # Sqlite3 and PostgreSQL pass.
 | |
|   # Is this MySQL bug?
 | |
|   def latest_changesets(path, rev, limit=10)
 | |
|     changesets.
 | |
|       includes(:user).
 | |
|       where(latest_changesets_cond(path, rev, limit)).
 | |
|       limit(limit).
 | |
|       order("#{Changeset.table_name}.id DESC").
 | |
|       all
 | |
|   end
 | |
| 
 | |
|   def latest_changesets_cond(path, rev, limit)
 | |
|     cond, args = [], []
 | |
|     if scm.branchmap.member? rev
 | |
|       # Mercurial named branch is *stable* in each revision.
 | |
|       # So, named branch can be stored in database.
 | |
|       # Mercurial provides *bookmark* which is equivalent with git branch.
 | |
|       # But, bookmark is not implemented.
 | |
|       cond << "#{Changeset.table_name}.scmid IN (?)"
 | |
|       # Revisions in root directory and sub directory are not equal.
 | |
|       # So, in order to get correct limit, we need to get all revisions.
 | |
|       # But, it is very heavy.
 | |
|       # Mercurial does not treat direcotry.
 | |
|       # So, "hg log DIR" is very heavy.
 | |
|       branch_limit = path.blank? ? limit : ( limit * 5 )
 | |
|       args << scm.nodes_in_branch(rev, :limit => branch_limit)
 | |
|     elsif last = rev ? find_changeset_by_name(scm.tagmap[rev] || rev) : nil
 | |
|       cond << "#{Changeset.table_name}.id <= ?"
 | |
|       args << last.id
 | |
|     end
 | |
|     unless path.blank?
 | |
|       cond << "EXISTS (SELECT * FROM #{Change.table_name}
 | |
|                  WHERE #{Change.table_name}.changeset_id = #{Changeset.table_name}.id
 | |
|                  AND (#{Change.table_name}.path = ?
 | |
|                        OR #{Change.table_name}.path LIKE ? ESCAPE ?))"
 | |
|       args << path.with_leading_slash
 | |
|       args << "#{path.with_leading_slash.gsub(%r{[%_\\]}) { |s| "\\#{s}" }}/%" << '\\'
 | |
|     end
 | |
|     [cond.join(' AND '), *args] unless cond.empty?
 | |
|   end
 | |
|   private :latest_changesets_cond
 | |
| 
 | |
|   def fetch_changesets
 | |
|     return if scm.info.nil?
 | |
|     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|
 | |
|       scm.each_revision('', i, [i + FETCH_AT_ONCE - 1, scm_rev].min) do |re|
 | |
|         transaction do
 | |
|           parents = (re.parents || []).collect{|rp| find_changeset_by_name(rp)}.compact
 | |
|           cs = Changeset.create(:repository   => self,
 | |
|                                 :revision     => re.revision,
 | |
|                                 :scmid        => re.scmid,
 | |
|                                 :committer    => re.author,
 | |
|                                 :committed_on => re.time,
 | |
|                                 :comments     => re.message,
 | |
|                                 :parents      => parents)
 | |
|           unless cs.new_record?
 | |
|             re.paths.each { |e| cs.create_change(e) }
 | |
|           end
 | |
|         end
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| end
 |