repository.rb 6.69 KB
Newer Older
1
class Repository
2 3
  include Gitlab::ShellAdapter

4
  attr_accessor :raw_repository, :path_with_namespace
5

6
  def initialize(path_with_namespace, default_branch = nil)
7
    @path_with_namespace = path_with_namespace
8
    @raw_repository = Gitlab::Git::Repository.new(path_to_repo) if path_with_namespace
9 10 11 12
  rescue Gitlab::Git::Repository::NoRepository
    nil
  end

13
  # Return absolute path to repository
14
  def path_to_repo
15 16 17
    @path_to_repo ||= File.expand_path(
      File.join(Gitlab.config.gitlab_shell.repos_path, path_with_namespace + ".git")
    )
18 19
  end

20 21 22 23 24 25
  def exists?
    raw_repository
  end

  def empty?
    raw_repository.empty?
26 27
  end

28
  def commit(id = 'HEAD')
29
    return nil unless raw_repository
30
    commit = Gitlab::Git::Commit.find(raw_repository, id)
31 32 33 34
    commit = Commit.new(commit) if commit
    commit
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
35
  def commits(ref, path = nil, limit = nil, offset = nil, skip_merges = false)
36 37 38 39 40 41 42
    commits = Gitlab::Git::Commit.where(
      repo: raw_repository,
      ref: ref,
      path: path,
      limit: limit,
      offset: offset,
    )
43
    commits = Commit.decorate(commits) if commits.present?
44 45 46
    commits
  end

47 48
  def commits_between(from, to)
    commits = Gitlab::Git::Commit.between(raw_repository, from, to)
49
    commits = Commit.decorate(commits) if commits.present?
50 51 52
    commits
  end

53 54 55 56 57 58 59 60
  def find_branch(name)
    branches.find { |branch| branch.name == name }
  end

  def find_tag(name)
    tags.find { |tag| tag.name == name }
  end

61 62 63 64 65 66
  def add_branch(branch_name, ref)
    Rails.cache.delete(cache_key(:branch_names))

    gitlab_shell.add_branch(path_with_namespace, branch_name, ref)
  end

67
  def add_tag(tag_name, ref, message = nil)
68 69
    Rails.cache.delete(cache_key(:tag_names))

70
    gitlab_shell.add_tag(path_with_namespace, tag_name, ref, message)
71 72
  end

73
  def rm_branch(branch_name)
74 75
    Rails.cache.delete(cache_key(:branch_names))

76 77 78
    gitlab_shell.rm_branch(path_with_namespace, branch_name)
  end

79
  def rm_tag(tag_name)
80 81
    Rails.cache.delete(cache_key(:tag_names))

82 83 84
    gitlab_shell.rm_tag(path_with_namespace, tag_name)
  end

85 86 87 88 89 90 91 92 93 94 95 96
  def round_commit_count
    if commit_count > 10000
      '10000+'
    elsif commit_count > 5000
      '5000+'
    elsif commit_count > 1000
      '1000+'
    else
      commit_count
    end
  end

97 98 99 100 101 102 103 104 105 106 107 108
  def branch_names
    Rails.cache.fetch(cache_key(:branch_names)) do
      raw_repository.branch_names
    end
  end

  def tag_names
    Rails.cache.fetch(cache_key(:tag_names)) do
      raw_repository.tag_names
    end
  end

109 110
  def commit_count
    Rails.cache.fetch(cache_key(:commit_count)) do
111
      begin
112
        raw_repository.commit_count(self.root_ref)
113 114 115
      rescue
        0
      end
116
    end
117 118
  end

119 120 121 122 123 124 125 126 127 128
  # Return repo size in megabytes
  # Cached in redis
  def size
    Rails.cache.fetch(cache_key(:size)) do
      raw_repository.size
    end
  end

  def expire_cache
    Rails.cache.delete(cache_key(:size))
129 130
    Rails.cache.delete(cache_key(:branch_names))
    Rails.cache.delete(cache_key(:tag_names))
131
    Rails.cache.delete(cache_key(:commit_count))
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
132
    Rails.cache.delete(cache_key(:graph_log))
133
    Rails.cache.delete(cache_key(:readme))
134
    Rails.cache.delete(cache_key(:version))
135
    Rails.cache.delete(cache_key(:contribution_guide))
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
136 137 138
  end

  def graph_log
139
    Rails.cache.fetch(cache_key(:graph_log)) do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
140
      stats = Gitlab::Git::GitStats.new(raw_repository, root_ref, Gitlab.config.git.timeout)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
141 142
      stats.parsed_log
    end
143 144 145 146 147 148
  end

  def cache_key(type)
    "#{type}:#{path_with_namespace}"
  end

149 150 151 152
  def method_missing(m, *args, &block)
    raw_repository.send(m, *args, &block)
  end

153
  def respond_to?(method)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
154
    return true if raw_repository.respond_to?(method)
155 156 157

    super
  end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
158 159 160 161

  def blob_at(sha, path)
    Gitlab::Git::Blob.find(self, sha, path)
  end
162

163 164 165 166
  def blob_by_oid(oid)
    Gitlab::Git::Blob.raw(self, oid)
  end

167 168
  def readme
    Rails.cache.fetch(cache_key(:readme)) do
169
      tree(:head).readme
170 171
    end
  end
172

173 174 175 176 177 178 179 180
  def version
    Rails.cache.fetch(cache_key(:version)) do
      tree(:head).blobs.find do |file|
        file.name.downcase == 'version'
      end
    end
  end

181 182 183 184 185 186
  def contribution_guide
    Rails.cache.fetch(cache_key(:contribution_guide)) do
      tree(:head).contribution_guide
    end
  end

187 188 189 190 191 192 193 194 195 196 197
  def head_commit
    commit(self.root_ref)
  end

  def tree(sha = :head, path = nil)
    if sha == :head
      sha = head_commit.sha
    end

    Tree.new(self, sha, path)
  end
198 199

  def blob_at_branch(branch_name, path)
200
    last_commit = commit(branch_name)
201

202 203 204 205 206
    if last_commit
      blob_at(last_commit.sha, path)
    else
      nil
    end
207
  end
208 209 210 211 212 213 214 215

  # Returns url for submodule
  #
  # Ex.
  #   @repository.submodule_url_for('master', 'rack')
  #   # => git@localhost:rack.git
  #
  def submodule_url_for(ref, path)
216
    if submodules(ref).any?
217 218 219 220 221 222 223
      submodule = submodules(ref)[path]

      if submodule
        submodule['url']
      end
    end
  end
224 225

  def last_commit_for_path(sha, path)
226 227 228
    args = %W(git rev-list --max-count 1 #{sha} -- #{path})
    sha = Gitlab::Popen.popen(args, path_to_repo).first.strip
    commit(sha)
229
  end
230 231 232

  # Remove archives older than 2 hours
  def clean_old_archives
233 234
    repository_downloads_path = Gitlab.config.gitlab.repository_downloads_path
    Gitlab::Popen.popen(%W(find #{repository_downloads_path} -not -path #{repository_downloads_path} -mmin +120 -delete))
235
  end
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250

  def branches_sorted_by(value)
    case value
    when 'recently_updated'
      branches.sort do |a, b|
        commit(b.target).committed_date <=> commit(a.target).committed_date
      end
    when 'last_updated'
      branches.sort do |a, b|
        commit(a.target).committed_date <=> commit(b.target).committed_date
      end
    else
      branches
    end
  end
251 252 253 254

  def contributors
    log = graph_log.group_by { |i| i[:author_email] }

255 256 257
    log.map do |email, contributions|
      contributor = Gitlab::Contributor.new
      contributor.email = email
258 259

      contributions.each do |contribution|
260 261
        if contributor.name.blank?
          contributor.name = contribution[:author_name]
262 263
        end

264 265 266
        contributor.commits += 1
        contributor.additions += contribution[:additions] || 0
        contributor.deletions += contribution[:deletions] || 0
267 268
      end

269 270
      contributor
    end
271
  end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287

  def blob_for_diff(commit, diff)
    file = blob_at(commit.id, diff.new_path)

    unless file
      file = prev_blob_for_diff(commit, diff)
    end

    file
  end

  def prev_blob_for_diff(commit, diff)
    if commit.parent_id
      blob_at(commit.parent_id, diff.old_path)
    end
  end
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304

  def branch_names_contains(sha)
    args = %W(git branch --contains #{sha})
    names = Gitlab::Popen.popen(args, path_to_repo).first

    if names.respond_to?(:split)
      names = names.split("\n").map(&:strip)

      names.each do |name|
        name.slice! '* '
      end

      names
    else
      []
    end
  end
305
end