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 / .svn / text-base / repository.rb.svn-base @ 441:cbce1fd3b1b7

History | View | Annotate | Download (8.69 KB)

1 441:cbce1fd3b1b7 Chris
# Redmine - project management software
2
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 0:513646585e45 Chris
#
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 441:cbce1fd3b1b7 Chris
#
9 0:513646585e45 Chris
# 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 441:cbce1fd3b1b7 Chris
#
14 0:513646585e45 Chris
# 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 245:051f544170fe Chris
  include Redmine::Ciphering
20 441:cbce1fd3b1b7 Chris
21 0:513646585e45 Chris
  belongs_to :project
22
  has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC"
23
  has_many :changes, :through => :changesets
24 441:cbce1fd3b1b7 Chris
25
  serialize :extra_info
26
27 0:513646585e45 Chris
  # Raw SQL to delete changesets and changes in the database
28
  # has_many :changesets, :dependent => :destroy is too slow for big repositories
29
  before_destroy :clear_changesets
30 441:cbce1fd3b1b7 Chris
31 245:051f544170fe Chris
  validates_length_of :password, :maximum => 255, :allow_nil => true
32 0:513646585e45 Chris
  # Checks if the SCM is enabled when creating a repository
33
  validate_on_create { |r| r.errors.add(:type, :invalid) unless Setting.enabled_scm.include?(r.class.name.demodulize) }
34 245:051f544170fe Chris
35 441:cbce1fd3b1b7 Chris
  def self.human_attribute_name(attribute_key_name)
36
    attr_name = attribute_key_name
37
    if attr_name == "log_encoding"
38
      attr_name = "commit_logs_encoding"
39
    end
40
    super(attr_name)
41
  end
42
43 0:513646585e45 Chris
  # Removes leading and trailing whitespace
44
  def url=(arg)
45
    write_attribute(:url, arg ? arg.to_s.strip : nil)
46
  end
47 245:051f544170fe Chris
48 0:513646585e45 Chris
  # Removes leading and trailing whitespace
49
  def root_url=(arg)
50
    write_attribute(:root_url, arg ? arg.to_s.strip : nil)
51
  end
52 441:cbce1fd3b1b7 Chris
53 245:051f544170fe Chris
  def password
54
    read_ciphered_attribute(:password)
55
  end
56 441:cbce1fd3b1b7 Chris
57 245:051f544170fe Chris
  def password=(arg)
58
    write_ciphered_attribute(:password, arg)
59
  end
60
61
  def scm_adapter
62
    self.class.scm_adapter_class
63
  end
64 0:513646585e45 Chris
65
  def scm
66 245:051f544170fe Chris
    @scm ||= self.scm_adapter.new(url, root_url,
67
                                  login, password, path_encoding)
68 0:513646585e45 Chris
    update_attribute(:root_url, @scm.root_url) if root_url.blank?
69
    @scm
70
  end
71 245:051f544170fe Chris
72 0:513646585e45 Chris
  def scm_name
73
    self.class.scm_name
74
  end
75 245:051f544170fe Chris
76 441:cbce1fd3b1b7 Chris
  def merge_extra_info(arg)
77
    h = extra_info || {}
78
    return h if arg.nil?
79
    h.merge!(arg)
80
    write_attribute(:extra_info, h)
81
  end
82
83
  def report_last_commit
84
    true
85
  end
86
87 0:513646585e45 Chris
  def supports_cat?
88
    scm.supports_cat?
89
  end
90
91
  def supports_annotate?
92
    scm.supports_annotate?
93
  end
94 441:cbce1fd3b1b7 Chris
95
  def supports_all_revisions?
96
    true
97
  end
98
99
  def supports_directory_revisions?
100
    false
101
  end
102
103 0:513646585e45 Chris
  def entry(path=nil, identifier=nil)
104
    scm.entry(path, identifier)
105
  end
106 441:cbce1fd3b1b7 Chris
107 0:513646585e45 Chris
  def entries(path=nil, identifier=nil)
108
    scm.entries(path, identifier)
109
  end
110
111
  def branches
112
    scm.branches
113
  end
114
115
  def tags
116
    scm.tags
117
  end
118
119
  def default_branch
120
    scm.default_branch
121
  end
122 441:cbce1fd3b1b7 Chris
123 0:513646585e45 Chris
  def properties(path, identifier=nil)
124
    scm.properties(path, identifier)
125
  end
126 441:cbce1fd3b1b7 Chris
127 0:513646585e45 Chris
  def cat(path, identifier=nil)
128
    scm.cat(path, identifier)
129
  end
130 441:cbce1fd3b1b7 Chris
131 0:513646585e45 Chris
  def diff(path, rev, rev_to)
132
    scm.diff(path, rev, rev_to)
133
  end
134 119:8661b858af72 Chris
135
  def diff_format_revisions(cs, cs_to, sep=':')
136
    text = ""
137
    text << cs_to.format_identifier + sep if cs_to
138
    text << cs.format_identifier if cs
139
    text
140
  end
141
142 0:513646585e45 Chris
  # Returns a path relative to the url of the repository
143
  def relative_path(path)
144
    path
145
  end
146 128:07fa8a8b56a8 Chris
147 0:513646585e45 Chris
  # Finds and returns a revision with a number or the beginning of a hash
148
  def find_changeset_by_name(name)
149 128:07fa8a8b56a8 Chris
    return nil if name.blank?
150 441:cbce1fd3b1b7 Chris
    changesets.find(:first, :conditions => (name.match(/^\d*$/) ?
151
          ["revision = ?", name.to_s] : ["revision LIKE ?", name + '%']))
152 0:513646585e45 Chris
  end
153 128:07fa8a8b56a8 Chris
154 0:513646585e45 Chris
  def latest_changeset
155
    @latest_changeset ||= changesets.find(:first)
156
  end
157
158
  # Returns the latest changesets for +path+
159
  # Default behaviour is to search in cached changesets
160
  def latest_changesets(path, rev, limit=10)
161
    if path.blank?
162 441:cbce1fd3b1b7 Chris
      changesets.find(
163
         :all,
164
         :include => :user,
165
         :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
166
         :limit => limit)
167 0:513646585e45 Chris
    else
168 441:cbce1fd3b1b7 Chris
      changes.find(
169
         :all,
170
         :include => {:changeset => :user},
171
         :conditions => ["path = ?", path.with_leading_slash],
172
         :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
173
         :limit => limit
174
       ).collect(&:changeset)
175 0:513646585e45 Chris
    end
176
  end
177 441:cbce1fd3b1b7 Chris
178 0:513646585e45 Chris
  def scan_changesets_for_issue_ids
179
    self.changesets.each(&:scan_comment_for_issue_ids)
180
  end
181
182
  # Returns an array of committers usernames and associated user_id
183
  def committers
184 441:cbce1fd3b1b7 Chris
    @committers ||= Changeset.connection.select_rows(
185
         "SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}")
186 0:513646585e45 Chris
  end
187 441:cbce1fd3b1b7 Chris
188 0:513646585e45 Chris
  # Maps committers username to a user ids
189
  def committer_ids=(h)
190
    if h.is_a?(Hash)
191
      committers.each do |committer, user_id|
192
        new_user_id = h[committer]
193
        if new_user_id && (new_user_id.to_i != user_id.to_i)
194
          new_user_id = (new_user_id.to_i > 0 ? new_user_id.to_i : nil)
195 441:cbce1fd3b1b7 Chris
          Changeset.update_all(
196
               "user_id = #{ new_user_id.nil? ? 'NULL' : new_user_id }",
197
               ["repository_id = ? AND committer = ?", id, committer])
198 0:513646585e45 Chris
        end
199
      end
200 441:cbce1fd3b1b7 Chris
      @committers            = nil
201 0:513646585e45 Chris
      @found_committer_users = nil
202
      true
203
    else
204
      false
205
    end
206
  end
207 441:cbce1fd3b1b7 Chris
208 0:513646585e45 Chris
  # Returns the Redmine User corresponding to the given +committer+
209
  # It will return nil if the committer is not yet mapped and if no User
210
  # with the same username or email was found
211
  def find_committer_user(committer)
212
    unless committer.blank?
213
      @found_committer_users ||= {}
214
      return @found_committer_users[committer] if @found_committer_users.has_key?(committer)
215 441:cbce1fd3b1b7 Chris
216 0:513646585e45 Chris
      user = nil
217
      c = changesets.find(:first, :conditions => {:committer => committer}, :include => :user)
218
      if c && c.user
219
        user = c.user
220
      elsif committer.strip =~ /^([^<]+)(<(.*)>)?$/
221
        username, email = $1.strip, $3
222
        u = User.find_by_login(username)
223
        u ||= User.find_by_mail(email) unless email.blank?
224
        user = u
225
      end
226
      @found_committer_users[committer] = user
227
      user
228
    end
229
  end
230 245:051f544170fe Chris
231
  def repo_log_encoding
232
    encoding = log_encoding.to_s.strip
233
    encoding.blank? ? 'UTF-8' : encoding
234
  end
235
236 0:513646585e45 Chris
  # Fetches new changesets for all repositories of active projects
237
  # Can be called periodically by an external script
238
  # eg. ruby script/runner "Repository.fetch_changesets"
239
  def self.fetch_changesets
240
    Project.active.has_module(:repository).find(:all, :include => :repository).each do |project|
241
      if project.repository
242 245:051f544170fe Chris
        begin
243
          project.repository.fetch_changesets
244
        rescue Redmine::Scm::Adapters::CommandFailed => e
245
          logger.error "scm: error during fetching changesets: #{e.message}"
246
        end
247 0:513646585e45 Chris
      end
248
    end
249
  end
250 245:051f544170fe Chris
251 0:513646585e45 Chris
  # scan changeset comments to find related and fixed issues for all repositories
252
  def self.scan_changesets_for_issue_ids
253
    find(:all).each(&:scan_changesets_for_issue_ids)
254
  end
255
256
  def self.scm_name
257
    'Abstract'
258
  end
259 441:cbce1fd3b1b7 Chris
260 0:513646585e45 Chris
  def self.available_scm
261
    subclasses.collect {|klass| [klass.scm_name, klass.name]}
262
  end
263 245:051f544170fe Chris
264 0:513646585e45 Chris
  def self.factory(klass_name, *args)
265
    klass = "Repository::#{klass_name}".constantize
266
    klass.new(*args)
267
  rescue
268
    nil
269
  end
270 245:051f544170fe Chris
271
  def self.scm_adapter_class
272
    nil
273
  end
274
275
  def self.scm_command
276
    ret = ""
277
    begin
278
      ret = self.scm_adapter_class.client_command if self.scm_adapter_class
279 441:cbce1fd3b1b7 Chris
    rescue Exception => e
280 245:051f544170fe Chris
      logger.error "scm: error during get command: #{e.message}"
281
    end
282
    ret
283
  end
284
285
  def self.scm_version_string
286
    ret = ""
287
    begin
288
      ret = self.scm_adapter_class.client_version_string if self.scm_adapter_class
289 441:cbce1fd3b1b7 Chris
    rescue Exception => e
290 245:051f544170fe Chris
      logger.error "scm: error during get version string: #{e.message}"
291
    end
292
    ret
293
  end
294
295
  def self.scm_available
296
    ret = false
297
    begin
298 441:cbce1fd3b1b7 Chris
      ret = self.scm_adapter_class.client_available if self.scm_adapter_class
299
    rescue Exception => e
300 245:051f544170fe Chris
      logger.error "scm: error during get scm available: #{e.message}"
301
    end
302
    ret
303
  end
304
305 0:513646585e45 Chris
  private
306 245:051f544170fe Chris
307 0:513646585e45 Chris
  def before_save
308
    # Strips url and root_url
309
    url.strip!
310
    root_url.strip!
311
    true
312
  end
313 441:cbce1fd3b1b7 Chris
314 0:513646585e45 Chris
  def clear_changesets
315
    cs, ch, ci = Changeset.table_name, Change.table_name, "#{table_name_prefix}changesets_issues#{table_name_suffix}"
316
    connection.delete("DELETE FROM #{ch} WHERE #{ch}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
317
    connection.delete("DELETE FROM #{ci} WHERE #{ci}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
318
    connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}")
319
  end
320
end