mercurial.rb
158 lines
| 5.4 KiB
| text/x-ruby
|
RubyLexer
|
r5636 | # Redmine - project management software | ||
|
r9453 | # Copyright (C) 2006-2012 Jean-Philippe Lang | ||
|
r556 | # | ||
# 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. | ||||
|
r5636 | # | ||
|
r556 | # 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. | ||||
|
r5636 | # | ||
|
r556 | # 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 | ||||
|
r4491 | # sort changesets by revision number | ||
|
r5811 | has_many :changesets, | ||
:order => "#{Changeset.table_name}.id DESC", | ||||
:foreign_key => 'repository_id' | ||||
|
r4491 | |||
|
r5811 | attr_protected :root_url | ||
|
r556 | validates_presence_of :url | ||
|
r5811 | # number of changesets to fetch at once | ||
FETCH_AT_ONCE = 100 | ||||
|
r4729 | |||
|
r8166 | def self.human_attribute_name(attribute_key_name, *args) | ||
|
r8856 | attr_name = attribute_key_name.to_s | ||
|
r5409 | if attr_name == "url" | ||
attr_name = "path_to_repository" | ||||
end | ||||
|
r8166 | super(attr_name, *args) | ||
|
r4855 | end | ||
|
r4702 | def self.scm_adapter_class | ||
|
r556 | Redmine::Scm::Adapters::MercurialAdapter | ||
end | ||||
|
r4575 | |||
|
r556 | def self.scm_name | ||
'Mercurial' | ||||
end | ||||
|
r4575 | |||
|
r5024 | def supports_directory_revisions? | ||
true | ||||
end | ||||
|
r7598 | def supports_revision_graph? | ||
true | ||||
end | ||||
|
r4842 | def repo_log_encoding | ||
'UTF-8' | ||||
end | ||||
|
r4575 | # Returns the readable identifier for the given mercurial changeset | ||
def self.format_changeset_identifier(changeset) | ||||
|
r4577 | "#{changeset.revision}:#{changeset.scmid}" | ||
|
r4575 | end | ||
# Returns the identifier for the given Mercurial changeset | ||||
def self.changeset_identifier(changeset) | ||||
changeset.scmid | ||||
end | ||||
|
r4579 | def diff_format_revisions(cs, cs_to, sep=':') | ||
super(cs, cs_to, ' ') | ||||
end | ||||
|
r4534 | # Finds and returns a revision with a number or the beginning of a hash | ||
def find_changeset_by_name(name) | ||||
|
r8818 | return nil if name.blank? | ||
s = name.to_s | ||||
if /[^\d]/ =~ s or s.size > 8 | ||||
|
r9629 | cs = changesets.where(:scmid => s).first | ||
|
r4534 | else | ||
|
r9629 | cs = changesets.where(:revision => s).first | ||
|
r4534 | end | ||
|
r9629 | return cs if cs | ||
changesets.where('scmid LIKE ?', "#{s}%").first | ||||
|
r4534 | end | ||
|
r4491 | # Returns the latest changesets for +path+; sorted by revision number | ||
|
r4971 | # | ||
# 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? | ||||
|
r4491 | def latest_changesets(path, rev, limit=10) | ||
|
r5811 | changesets.find(:all, | ||
:include => :user, | ||||
|
r5003 | :conditions => latest_changesets_cond(path, rev, limit), | ||
|
r5811 | :limit => limit, | ||
:order => "#{Changeset.table_name}.id DESC") | ||||
|
r5003 | end | ||
def latest_changesets_cond(path, rev, limit) | ||||
cond, args = [], [] | ||||
|
r5007 | 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. | ||||
|
r5026 | # 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) | ||||
|
r5007 | elsif last = rev ? find_changeset_by_name(scm.tagmap[rev] || rev) : nil | ||
|
r5003 | cond << "#{Changeset.table_name}.id <= ?" | ||
args << last.id | ||||
|
r4491 | end | ||
|
r5003 | unless path.blank? | ||
cond << "EXISTS (SELECT * FROM #{Change.table_name} | ||||
WHERE #{Change.table_name}.changeset_id = #{Changeset.table_name}.id | ||||
|
r5636 | AND (#{Change.table_name}.path = ? | ||
|
r5003 | OR #{Change.table_name}.path LIKE ? ESCAPE ?))" | ||
args << path.with_leading_slash | ||||
|
r5810 | args << "#{path.with_leading_slash.gsub(%r{[%_\\]}) { |s| "\\#{s}" }}/%" << '\\' | ||
|
r5003 | end | ||
[cond.join(' AND '), *args] unless cond.empty? | ||||
|
r4491 | end | ||
|
r5003 | private :latest_changesets_cond | ||
|
r4491 | |||
|
r556 | def fetch_changesets | ||
|
r6126 | return if scm.info.nil? | ||
|
r4729 | scm_rev = scm.info.lastrev.revision.to_i | ||
|
r5811 | db_rev = latest_changeset ? latest_changeset.revision.to_i : -1 | ||
|
r4729 | 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| | ||||
|
r9154 | scm.each_revision('', i, [i + FETCH_AT_ONCE - 1, scm_rev].min) do |re| | ||
transaction do | ||||
|
r9177 | parents = (re.parents || []).collect{|rp| find_changeset_by_name(rp)}.compact | ||
|
r5811 | cs = Changeset.create(:repository => self, | ||
:revision => re.revision, | ||||
:scmid => re.scmid, | ||||
:committer => re.author, | ||||
|
r4729 | :committed_on => re.time, | ||
|
r9177 | :comments => re.message, | ||
:parents => parents) | ||||
|
r9154 | unless cs.new_record? | ||
re.paths.each { |e| cs.create_change(e) } | ||||
|
r7593 | end | ||
|
r556 | end | ||
end | ||||
end | ||||
end | ||||
end | ||||