To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / app / models / repository.rb @ 437:102056ec2de9

History | View | Annotate | Download (6.82 KB)

1 0:513646585e45 Chris
# redMine - project management software
2
# Copyright (C) 2006-2007  Jean-Philippe Lang
3
#
4
# This program is free software; you can redistribute it and/or
5
# modify it under the terms of the GNU General Public License
6
# as published by the Free Software Foundation; either version 2
7
# of the License, or (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17
18
class Repository < ActiveRecord::Base
19
  belongs_to :project
20 3:7c48bad7d85d Chris
  has_many :changesets, :order => "#{Changeset.table_name}.id DESC"
21 0:513646585e45 Chris
  has_many :changes, :through => :changesets
22
23
  # Raw SQL to delete changesets and changes in the database
24
  # has_many :changesets, :dependent => :destroy is too slow for big repositories
25
  before_destroy :clear_changesets
26
27
  # Checks if the SCM is enabled when creating a repository
28
  validate_on_create { |r| r.errors.add(:type, :invalid) unless Setting.enabled_scm.include?(r.class.name.demodulize) }
29
30
  # Removes leading and trailing whitespace
31
  def url=(arg)
32
    write_attribute(:url, arg ? arg.to_s.strip : nil)
33
  end
34
35
  # Removes leading and trailing whitespace
36
  def root_url=(arg)
37
    write_attribute(:root_url, arg ? arg.to_s.strip : nil)
38
  end
39
40
  def scm
41
    @scm ||= self.scm_adapter.new url, root_url, login, password
42
    update_attribute(:root_url, @scm.root_url) if root_url.blank?
43
    @scm
44
  end
45
46
  def scm_name
47
    self.class.scm_name
48
  end
49
50
  def supports_cat?
51
    scm.supports_cat?
52
  end
53
54
  def supports_annotate?
55
    scm.supports_annotate?
56
  end
57
58
  def entry(path=nil, identifier=nil)
59
    scm.entry(path, identifier)
60
  end
61
62
  def entries(path=nil, identifier=nil)
63
    scm.entries(path, identifier)
64
  end
65
66
  def branches
67
    scm.branches
68
  end
69
70
  def tags
71
    scm.tags
72
  end
73
74
  def default_branch
75
    scm.default_branch
76
  end
77
78
  def properties(path, identifier=nil)
79
    scm.properties(path, identifier)
80
  end
81
82
  def cat(path, identifier=nil)
83
    scm.cat(path, identifier)
84
  end
85
86
  def diff(path, rev, rev_to)
87
    scm.diff(path, rev, rev_to)
88
  end
89
90
  # Returns a path relative to the url of the repository
91
  def relative_path(path)
92
    path
93
  end
94
95
  # Finds and returns a revision with a number or the beginning of a hash
96
  def find_changeset_by_name(name)
97 3:7c48bad7d85d Chris
    # TODO: is this query efficient enough? can we write as single query?
98
    e = changesets.find(:first, :conditions => ['revision = ? OR scmid = ?', name.to_s, name.to_s])
99
    return e if e
100
    changesets.find(:first, :conditions => ['scmid LIKE ?', "#{name}%"])
101 0:513646585e45 Chris
  end
102
103
  def latest_changeset
104
    @latest_changeset ||= changesets.find(:first)
105
  end
106
107
  # Returns the latest changesets for +path+
108
  # Default behaviour is to search in cached changesets
109
  def latest_changesets(path, rev, limit=10)
110
    if path.blank?
111
      changesets.find(:all, :include => :user,
112
                            :limit => limit)
113
    else
114
      changes.find(:all, :include => {:changeset => :user},
115
                         :conditions => ["path = ?", path.with_leading_slash],
116 3:7c48bad7d85d Chris
                         :order => "#{Changeset.table_name}.id DESC",
117 0:513646585e45 Chris
                         :limit => limit).collect(&:changeset)
118
    end
119
  end
120
121
  def scan_changesets_for_issue_ids
122
    self.changesets.each(&:scan_comment_for_issue_ids)
123
  end
124
125
  # Returns an array of committers usernames and associated user_id
126
  def committers
127
    @committers ||= Changeset.connection.select_rows("SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}")
128
  end
129
130
  # Maps committers username to a user ids
131
  def committer_ids=(h)
132
    if h.is_a?(Hash)
133
      committers.each do |committer, user_id|
134
        new_user_id = h[committer]
135
        if new_user_id && (new_user_id.to_i != user_id.to_i)
136
          new_user_id = (new_user_id.to_i > 0 ? new_user_id.to_i : nil)
137
          Changeset.update_all("user_id = #{ new_user_id.nil? ? 'NULL' : new_user_id }", ["repository_id = ? AND committer = ?", id, committer])
138
        end
139
      end
140
      @committers = nil
141
      @found_committer_users = nil
142
      true
143
    else
144
      false
145
    end
146
  end
147
148
  # Returns the Redmine User corresponding to the given +committer+
149
  # It will return nil if the committer is not yet mapped and if no User
150
  # with the same username or email was found
151
  def find_committer_user(committer)
152
    unless committer.blank?
153
      @found_committer_users ||= {}
154
      return @found_committer_users[committer] if @found_committer_users.has_key?(committer)
155
156
      user = nil
157
      c = changesets.find(:first, :conditions => {:committer => committer}, :include => :user)
158
      if c && c.user
159
        user = c.user
160
      elsif committer.strip =~ /^([^<]+)(<(.*)>)?$/
161
        username, email = $1.strip, $3
162
        u = User.find_by_login(username)
163
        u ||= User.find_by_mail(email) unless email.blank?
164
        user = u
165
      end
166
      @found_committer_users[committer] = user
167
      user
168
    end
169
  end
170
171
  # Fetches new changesets for all repositories of active projects
172
  # Can be called periodically by an external script
173
  # eg. ruby script/runner "Repository.fetch_changesets"
174
  def self.fetch_changesets
175
    Project.active.has_module(:repository).find(:all, :include => :repository).each do |project|
176
      if project.repository
177 3:7c48bad7d85d Chris
        begin
178
          project.repository.fetch_changesets
179
        rescue Redmine::Scm::Adapters::CommandFailed => e
180
          logger.error "Repository: error during fetching changesets: #{e.message}"
181
        end
182 0:513646585e45 Chris
      end
183
    end
184
  end
185
186
  # scan changeset comments to find related and fixed issues for all repositories
187
  def self.scan_changesets_for_issue_ids
188
    find(:all).each(&:scan_changesets_for_issue_ids)
189
  end
190
191
  def self.scm_name
192
    'Abstract'
193
  end
194
195
  def self.available_scm
196
    subclasses.collect {|klass| [klass.scm_name, klass.name]}
197
  end
198
199
  def self.factory(klass_name, *args)
200
    klass = "Repository::#{klass_name}".constantize
201
    klass.new(*args)
202
  rescue
203
    nil
204
  end
205
206 437:102056ec2de9 chris
  def clear_cache
207
    clear_changesets
208
  end
209
210 0:513646585e45 Chris
  private
211
212
  def before_save
213
    # Strips url and root_url
214
    url.strip!
215
    root_url.strip!
216
    true
217
  end
218
219
  def clear_changesets
220
    cs, ch, ci = Changeset.table_name, Change.table_name, "#{table_name_prefix}changesets_issues#{table_name_suffix}"
221
    connection.delete("DELETE FROM #{ch} WHERE #{ch}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
222
    connection.delete("DELETE FROM #{ci} WHERE #{ci}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
223
    connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}")
224
  end
225
end