repository.rb 20.3 KB
Newer Older
1 2
require 'securerandom'

3
class Repository
4 5
  class CommitError < StandardError; end

6 7
  include Gitlab::ShellAdapter

8
  attr_accessor :path_with_namespace, :project
9

10 11 12 13 14 15 16 17
  def self.clean_old_archives
    repository_downloads_path = Gitlab.config.gitlab.repository_downloads_path

    return unless File.directory?(repository_downloads_path)

    Gitlab::Popen.popen(%W(find #{repository_downloads_path} -not -path #{repository_downloads_path} -mmin +120 -delete))
  end

18
  def initialize(path_with_namespace, project)
19
    @path_with_namespace = path_with_namespace
20
    @project = project
21
  end
22

23 24
  def raw_repository
    return nil unless path_with_namespace
25

26 27 28 29 30
    @raw_repository ||= Gitlab::Git::Repository.new(path_to_repo)
  end

  def update_autocrlf_option
    raw_repository.autocrlf = :input if raw_repository.autocrlf != :input
31 32
  end

33
  # Return absolute path to repository
34
  def path_to_repo
35 36 37
    @path_to_repo ||= File.expand_path(
      File.join(Gitlab.config.gitlab_shell.repos_path, path_with_namespace + ".git")
    )
38 39
  end

40
  def exists?
41 42 43 44 45 46
    return false unless raw_repository

    raw_repository.rugged
    true
  rescue Gitlab::Git::Repository::NoRepository
    false
47 48 49
  end

  def empty?
50 51 52
    return @empty unless @empty.nil?

    @empty = cache.fetch(:empty?) { raw_repository.empty? }
53 54
  end

55 56 57 58 59 60 61 62 63 64
  #
  # Git repository can contains some hidden refs like:
  #   /refs/notes/*
  #   /refs/git-as-svn/*
  #   /refs/pulls/*
  # This refs by default not visible in project page and not cloned to client side.
  #
  # This method return true if repository contains some content visible in project page.
  #
  def has_visible_content?
65 66 67 68 69
    return @has_visible_content unless @has_visible_content.nil?

    @has_visible_content = cache.fetch(:has_visible_content?) do
      raw_repository.branch_count > 0
    end
70 71
  end

72
  def commit(id = 'HEAD')
73
    return nil unless exists?
74
    commit = Gitlab::Git::Commit.find(raw_repository, id)
75
    commit = Commit.new(commit, @project) if commit
76
    commit
77
  rescue Rugged::OdbError
78
    nil
79 80
  end

81
  def commits(ref, path = nil, limit = nil, offset = nil, skip_merges = false)
82
    options = {
83 84 85 86 87
      repo: raw_repository,
      ref: ref,
      path: path,
      limit: limit,
      offset: offset,
88 89
      # --follow doesn't play well with --skip. See:
      # https://gitlab.com/gitlab-org/gitlab-ce/issues/3574#note_3040520
90 91
      follow: false,
      skip_merges: skip_merges
92 93 94
    }

    commits = Gitlab::Git::Commit.where(options)
95
    commits = Commit.decorate(commits, @project) if commits.present?
96 97 98
    commits
  end

99 100
  def commits_between(from, to)
    commits = Gitlab::Git::Commit.between(raw_repository, from, to)
101
    commits = Commit.decorate(commits, @project) if commits.present?
102 103 104
    commits
  end

105 106 107
  def find_commits_by_message(query, ref = nil, path = nil, limit = 1000, offset = 0)
    ref ||= root_ref

108
    # Limited to 1000 commits for now, could be parameterized?
109 110
    args = %W(#{Gitlab.config.git.bin_path} log #{ref} --pretty=%H --skip #{offset} --max-count #{limit} --grep=#{query})
    args = args.concat(%W(-- #{path})) if path.present?
111

112 113
    git_log_results = Gitlab::Popen.popen(args, path_to_repo).first.lines.map(&:chomp)
    commits = git_log_results.map { |c| commit(c) }
114
    commits
115 116
  end

117
  def find_branch(name)
118
    raw_repository.branches.find { |branch| branch.name == name }
119 120 121
  end

  def find_tag(name)
122
    raw_repository.tags.find { |tag| tag.name == name }
123 124
  end

125 126 127 128 129 130 131 132 133 134
  def add_branch(user, branch_name, target)
    oldrev = Gitlab::Git::BLANK_SHA
    ref    = Gitlab::Git::BRANCH_REF_PREFIX + branch_name
    target = commit(target).try(:id)

    return false unless target

    GitHooksService.new.execute(user, path_to_repo, oldrev, target, ref) do
      rugged.branches.create(branch_name, target)
    end
135

136 137
    expire_branches_cache
    find_branch(branch_name)
138 139
  end

140
  def add_tag(tag_name, ref, message = nil)
141
    expire_tags_cache
142

143
    gitlab_shell.add_tag(path_with_namespace, tag_name, ref, message)
144 145
  end

146
  def rm_branch(user, branch_name)
147
    expire_branches_cache
148

149 150 151 152 153 154 155 156
    branch = find_branch(branch_name)
    oldrev = branch.try(:target)
    newrev = Gitlab::Git::BLANK_SHA
    ref    = Gitlab::Git::BRANCH_REF_PREFIX + branch_name

    GitHooksService.new.execute(user, path_to_repo, oldrev, newrev, ref) do
      rugged.branches.delete(branch_name)
    end
157

158 159
    expire_branches_cache
    true
160 161
  end

162
  def rm_tag(tag_name)
163
    expire_tags_cache
164

165 166 167
    gitlab_shell.rm_tag(path_with_namespace, tag_name)
  end

168
  def branch_names
169
    cache.fetch(:branch_names) { raw_repository.branch_names }
170 171 172
  end

  def tag_names
173
    cache.fetch(:tag_names) { raw_repository.tag_names }
174 175
  end

176
  def commit_count
177
    cache.fetch(:commit_count) do
178
      begin
179
        raw_repository.commit_count(self.root_ref)
180 181 182
      rescue
        0
      end
183
    end
184 185
  end

186 187 188
  # Return repo size in megabytes
  # Cached in redis
  def size
189
    cache.fetch(:size) { raw_repository.size }
190
  end
191

192
  def diverging_commit_counts(branch)
193
    root_ref_hash = raw_repository.rev_parse_target(root_ref).oid
Jeff Stubler's avatar
Jeff Stubler committed
194
    cache.fetch(:"diverging_commit_counts_#{branch.name}") do
195 196
      # Rugged seems to throw a `ReferenceError` when given branch_names rather
      # than SHA-1 hashes
197 198 199 200 201
      number_commits_behind = raw_repository.
        count_commits_between(branch.target, root_ref_hash)

      number_commits_ahead = raw_repository.
        count_commits_between(root_ref_hash, branch.target)
202

203 204 205
      { behind: number_commits_behind, ahead: number_commits_ahead }
    end
  end
206

207
  def cache_keys
208
    %i(size branch_names tag_names commit_count
209 210
       readme version contribution_guide changelog license)
  end
211

212 213 214 215 216 217
  def build_cache
    cache_keys.each do |key|
      unless cache.exist?(key)
        send(key)
      end
    end
218

219
    branches.each do |branch|
Jeff Stubler's avatar
Jeff Stubler committed
220
      unless cache.exist?(:"diverging_commit_counts_#{branch.name}")
221 222 223
        send(:diverging_commit_counts, branch)
      end
    end
224 225
  end

226 227 228 229 230 231 232 233 234 235
  def expire_tags_cache
    cache.expire(:tag_names)
    @tags = nil
  end

  def expire_branches_cache
    cache.expire(:branch_names)
    @branches = nil
  end

236
  def expire_cache(branch_name = nil)
237
    cache_keys.each do |key|
238 239
      cache.expire(key)
    end
240

241
    expire_branch_cache(branch_name)
242 243 244 245

    # This ensures this particular cache is flushed after the first commit to a
    # new repository.
    expire_emptiness_caches if empty?
246
  end
247

248 249 250 251 252 253 254 255 256
  # Expires _all_ caches, including those that would normally only be expired
  # under specific conditions.
  def expire_all_caches!
    expire_cache
    expire_root_ref_cache
    expire_emptiness_caches
    expire_has_visible_content_cache
  end

257 258 259 260 261 262 263 264 265 266 267 268
  def expire_branch_cache(branch_name = nil)
    # When we push to the root branch we have to flush the cache for all other
    # branches as their statistics are based on the commits relative to the
    # root branch.
    if !branch_name || branch_name == root_ref
      branches.each do |branch|
        cache.expire(:"diverging_commit_counts_#{branch.name}")
      end
    # In case a commit is pushed to a non-root branch we only have to flush the
    # cache for said branch.
    else
      cache.expire(:"diverging_commit_counts_#{branch_name}")
269
    end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
270 271
  end

272 273 274 275 276
  def expire_root_ref_cache
    cache.expire(:root_ref)
    @root_ref = nil
  end

277 278 279 280 281 282 283 284
  # Expires the cache(s) used to determine if a repository is empty or not.
  def expire_emptiness_caches
    cache.expire(:empty?)
    @empty = nil

    expire_has_visible_content_cache
  end

285 286 287 288 289
  def expire_has_visible_content_cache
    cache.expire(:has_visible_content?)
    @has_visible_content = nil
  end

290 291
  def rebuild_cache
    cache_keys.each do |key|
292
      cache.expire(key)
293
      send(key)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
294
    end
295

296
    branches.each do |branch|
Jeff Stubler's avatar
Jeff Stubler committed
297 298
      cache.expire(:"diverging_commit_counts_#{branch.name}")
      diverging_commit_counts(branch)
299
    end
300 301
  end

302 303 304 305
  def lookup_cache
    @lookup_cache ||= {}
  end

306 307 308 309
  def expire_branch_names
    cache.expire(:branch_names)
  end

310
  def method_missing(m, *args, &block)
311 312 313 314 315 316
    if m == :lookup && !block_given?
      lookup_cache[m] ||= {}
      lookup_cache[m][args.join(":")] ||= raw_repository.send(m, *args, &block)
    else
      raw_repository.send(m, *args, &block)
    end
317 318
  end

319 320
  def respond_to_missing?(method, include_private = false)
    raw_repository.respond_to?(method, include_private) || super
321
  end
322 323

  def blob_at(sha, path)
324 325 326
    unless Gitlab::Git.blank_ref?(sha)
      Gitlab::Git::Blob.find(self, sha, path)
    end
327
  end
328

329 330 331 332
  def blob_by_oid(oid)
    Gitlab::Git::Blob.raw(self, oid)
  end

333
  def readme
334
    cache.fetch(:readme) { tree(:head).readme }
335
  end
336

337
  def version
338
    cache.fetch(:version) do
339 340 341 342 343 344
      tree(:head).blobs.find do |file|
        file.name.downcase == 'version'
      end
    end
  end

345
  def contribution_guide
346 347 348 349 350 351
    cache.fetch(:contribution_guide) do
      tree(:head).blobs.find do |file|
        file.contributing?
      end
    end
  end
352 353 354 355

  def changelog
    cache.fetch(:changelog) do
      tree(:head).blobs.find do |file|
356
        file.name =~ /\A(changelog|history)/i
357 358
      end
    end
359 360
  end

361 362
  def license
    cache.fetch(:license) do
363
      licenses =  tree(:head).blobs.find_all do |file|
364
                    file.name =~ /\A(copying|license|licence)/i
365 366
                  end

367 368 369 370 371 372 373 374 375 376 377 378 379
      preferences = [
        /\Alicen[sc]e\z/i,        # LICENSE, LICENCE
        /\Alicen[sc]e\./i,        # LICENSE.md, LICENSE.txt
        /\Acopying\z/i,           # COPYING
        /\Acopying\.(?!lesser)/i, # COPYING.txt
        /Acopying.lesser/i        # COPYING.LESSER
      ]

      license = nil
      preferences.each do |r|
        license = licenses.find { |l| l.name =~ r }
        break if license
      end
380

381
      license
382
    end
383 384
  end

385
  def head_commit
386 387 388 389 390
    @head_commit ||= commit(self.root_ref)
  end

  def head_tree
    @head_tree ||= Tree.new(self, head_commit.sha, nil)
391 392 393 394
  end

  def tree(sha = :head, path = nil)
    if sha == :head
395 396 397 398 399
      if path.nil?
        return head_tree
      else
        sha = head_commit.sha
      end
400 401 402 403
    end

    Tree.new(self, sha, path)
  end
404 405

  def blob_at_branch(branch_name, path)
406
    last_commit = commit(branch_name)
407

408 409 410 411 412
    if last_commit
      blob_at(last_commit.sha, path)
    else
      nil
    end
413
  end
414 415 416 417 418 419 420 421

  # Returns url for submodule
  #
  # Ex.
  #   @repository.submodule_url_for('master', 'rack')
  #   # => git@localhost:rack.git
  #
  def submodule_url_for(ref, path)
422
    if submodules(ref).any?
423 424 425 426 427 428 429
      submodule = submodules(ref)[path]

      if submodule
        submodule['url']
      end
    end
  end
430 431

  def last_commit_for_path(sha, path)
432
    args = %W(#{Gitlab.config.git.bin_path} rev-list --max-count=1 #{sha} -- #{path})
433 434
    sha = Gitlab::Popen.popen(args, path_to_repo).first.strip
    commit(sha)
435
  end
436

437 438 439 440 441 442 443 444 445 446 447
  def next_patch_branch
    patch_branch_ids = self.branch_names.map do |n|
      result = n.match(/\Apatch-([0-9]+)\z/)
      result[1].to_i if result
    end.compact

    highest_patch_branch_id = patch_branch_ids.max || 0

    "patch-#{highest_patch_branch_id + 1}"
  end

448
  # Remove archives older than 2 hours
449 450 451 452 453 454 455 456 457 458 459 460 461 462
  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
463 464

  def contributors
465
    commits = self.commits(nil, nil, 2000, 0, true)
466

467
    commits.group_by(&:author_email).map do |email, commits|
468 469
      contributor = Gitlab::Contributor.new
      contributor.email = email
470

471
      commits.each do |commit|
472
        if contributor.name.blank?
473
          contributor.name = commit.author_name
474 475
        end

476
        contributor.commits += 1
477 478
      end

479 480
      contributor
    end
481
  end
482 483

  def blob_for_diff(commit, diff)
484
    blob_at(commit.id, diff.file_path)
485 486 487 488 489 490 491
  end

  def prev_blob_for_diff(commit, diff)
    if commit.parent_id
      blob_at(commit.parent_id, diff.old_path)
    end
  end
492

493 494
  def refs_contains_sha(ref_type, sha)
    args = %W(#{Gitlab.config.git.bin_path} #{ref_type} --contains #{sha})
495 496 497 498 499 500 501 502 503 504 505 506 507 508
    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
509

510 511 512
  def branch_names_contains(sha)
    refs_contains_sha('branch', sha)
  end
513

514 515
  def tag_names_contains(sha)
    refs_contains_sha('tag', sha)
516
  end
517

518 519 520 521 522 523 524 525 526
  def branches
    @branches ||= raw_repository.branches
  end

  def tags
    @tags ||= raw_repository.tags
  end

  def root_ref
527
    @root_ref ||= cache.fetch(:root_ref) { raw_repository.root_ref }
528 529
  end

Stan Hu's avatar
Stan Hu committed
530
  def commit_dir(user, path, message, branch)
531
    commit_with_hooks(user, branch) do |ref|
Stan Hu's avatar
Stan Hu committed
532 533 534 535 536 537 538 539 540 541 542 543 544
      committer = user_to_committer(user)
      options = {}
      options[:committer] = committer
      options[:author] = committer

      options[:commit] = {
        message: message,
        branch: ref,
      }

      raw_repository.mkdir(path, options)
    end
  end
545

Stan Hu's avatar
Stan Hu committed
546 547 548
  def commit_file(user, path, content, message, branch, update)
    commit_with_hooks(user, branch) do |ref|
      committer = user_to_committer(user)
549 550 551 552 553 554 555
      options = {}
      options[:committer] = committer
      options[:author] = committer
      options[:commit] = {
        message: message,
        branch: ref,
      }
556

557 558
      options[:file] = {
        content: content,
Stan Hu's avatar
Stan Hu committed
559 560
        path: path,
        update: update
561
      }
562

563 564
      Gitlab::Git::Blob.commit(raw_repository, options)
    end
565 566
  end

567
  def remove_file(user, path, message, branch)
568
    commit_with_hooks(user, branch) do |ref|
Stan Hu's avatar
Stan Hu committed
569
      committer = user_to_committer(user)
570 571 572 573 574 575 576
      options = {}
      options[:committer] = committer
      options[:author] = committer
      options[:commit] = {
        message: message,
        branch: ref
      }
577

578 579 580
      options[:file] = {
        path: path
      }
581

582 583
      Gitlab::Git::Blob.remove(raw_repository, options)
    end
584 585
  end

Stan Hu's avatar
Stan Hu committed
586
  def user_to_committer(user)
587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604
    {
      email: user.email,
      name: user.name,
      time: Time.now
    }
  end

  def can_be_merged?(source_sha, target_branch)
    our_commit = rugged.branches[target_branch].target
    their_commit = rugged.lookup(source_sha)

    if our_commit && their_commit
      !rugged.merge_commits(our_commit, their_commit).conflicts?
    else
      false
    end
  end

605
  def merge(user, source_sha, target_branch, options = {})
606 607 608 609 610 611 612 613 614
    our_commit = rugged.branches[target_branch].target
    their_commit = rugged.lookup(source_sha)

    raise "Invalid merge target" if our_commit.nil?
    raise "Invalid merge source" if their_commit.nil?

    merge_index = rugged.merge_commits(our_commit, their_commit)
    return false if merge_index.conflicts?

615 616 617 618 619 620
    commit_with_hooks(user, target_branch) do |ref|
      actual_options = options.merge(
        parents: [our_commit, their_commit],
        tree: merge_index.write_tree(rugged),
        update_ref: ref
      )
621

622 623
      Rugged::Commit.create(rugged, actual_options)
    end
624 625
  end

626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
  # apply patches represented by [] of commits
  def apply_patches(user, patches, target_branch, options = {})
    base_commit = rugged.branches[target_branch].target
    raise "Invalid apply target" if base_commit.nil?

    res = nil
    patches.each_with_index do |patch, i|
      patch = rugged.lookup(patch.id) # Gitlab::Commit -> Rugged::Commit
      args = [patch, base_commit]
      args << { mainline: 1 } if patch.parents.size > 1

      index = rugged.cherrypick_commit(*args)
      raise "Conflict while applying #{patch.oid} to #{base_commit.oid}" if index.conflicts?

      tree_id = index.write_tree(rugged)
      # no check for empty diff: i.e. cherry-pick --allow-empty

      committer = user_to_committer(user)
      commit_options = {
        message:    patch.message,
        author:     patch.author,
        committer:  committer,
        tree:       tree_id,
        parents:    [base_commit],
      }.merge(options)

      # for all patches except last just create commit object without updating
      # ref and invoking hooks
      if i + 1 < patches.length then
        base_commit = Rugged::Commit.create(rugged, commit_options)

      # for last patch make sure to update ref and invoke hooks
      else
        res = commit_with_hooks(user, target_branch) do |ref|
          commit_options["update_ref"] = ref
          Rugged::Commit.create(rugged, commit_options)
        end
      end

    end

    fail if res.nil? # assert
    res
  end

671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698
  def revert(user, commit, base_branch, target_branch = nil)
    source_sha    = find_branch(base_branch).target
    target_branch ||= base_branch
    args          = [commit.id, source_sha]
    args          << { mainline: 1 } if commit.merge_commit?

    revert_index = rugged.revert_commit(*args)
    return false if revert_index.conflicts?

    tree_id = revert_index.write_tree(rugged)
    return false unless diff_exists?(source_sha, tree_id)

    commit_with_hooks(user, target_branch) do |ref|
      committer = user_to_committer(user)
      source_sha = Rugged::Commit.create(rugged,
        message: commit.revert_message,
        author: committer,
        committer: committer,
        tree: tree_id,
        parents: [rugged.lookup(source_sha)],
        update_ref: ref)
    end
  end

  def diff_exists?(sha1, sha2)
    rugged.diff(sha1, sha2).size > 0
  end

699 700 701 702 703
  def merged_to_root_ref?(branch_name)
    branch_commit = commit(branch_name)
    root_ref_commit = commit(root_ref)

    if branch_commit
704
      is_ancestor?(branch_commit.id, root_ref_commit.id)
705 706 707 708 709
    else
      nil
    end
  end

710
  def merge_base(first_commit_id, second_commit_id)
711 712
    first_commit_id = commit(first_commit_id).try(:id) || first_commit_id
    second_commit_id = commit(second_commit_id).try(:id) || second_commit_id
713
    rugged.merge_base(first_commit_id, second_commit_id)
Douwe Maan's avatar
Douwe Maan committed
714 715
  rescue Rugged::ReferenceError
    nil
716 717
  end

718 719 720 721 722
  def is_ancestor?(ancestor_id, descendant_id)
    merge_base(ancestor_id, descendant_id) == ancestor_id
  end


723 724
  def search_files(query, ref)
    offset = 2
725
    args = %W(#{Gitlab.config.git.bin_path} grep -i -I -n --before-context #{offset} --after-context #{offset} -e #{query} #{ref || root_ref})
726 727 728
    Gitlab::Popen.popen(args, path_to_repo).first.scrub.split(/^--$/)
  end

729
  def parse_search_result(result)
730 731 732 733
    ref = nil
    filename = nil
    startline = 0

734
    result.each_line.each_with_index do |line, index|
735 736 737 738 739 740 741
      if line =~ /^.*:.*:\d+:/
        ref, filename, startline = line.split(':')
        startline = startline.to_i - index
        break
      end
    end

742
    data = ""
743

744 745 746
    result.each_line do |line|
      data << line.sub(ref, '').sub(filename, '').sub(/^:-\d+-/, '').sub(/^::\d+:/, '')
    end
747 748 749 750 751 752 753 754 755

    OpenStruct.new(
      filename: filename,
      ref: ref,
      startline: startline,
      data: data
    )
  end

756
  def fetch_ref(source_path, source_ref, target_ref)
757
    args = %W(#{Gitlab.config.git.bin_path} fetch -f #{source_path} #{source_ref}:#{target_ref})
758 759 760
    Gitlab::Popen.popen(args, path_to_repo)
  end

761
  def with_tmp_ref(oldrev = nil)
762 763 764
    random_string = SecureRandom.hex
    tmp_ref = "refs/tmp/#{random_string}/head"

765
    if oldrev && !Gitlab::Git.blank_ref?(oldrev)
766 767 768 769
      rugged.references.create(tmp_ref, oldrev)
    end

    # Make commit in tmp ref
770 771 772 773 774 775
    yield(tmp_ref)
  ensure
    rugged.references.delete(tmp_ref) rescue nil
  end

  def commit_with_hooks(current_user, branch)
776 777
    update_autocrlf_option

778 779
    oldrev = Gitlab::Git::BLANK_SHA
    ref = Gitlab::Git::BRANCH_REF_PREFIX + branch
780
    target_branch = find_branch(branch)
781
    was_empty = empty?
782

783 784
    if !was_empty && target_branch
      oldrev = target_branch.target
785 786
    end

787 788 789 790 791 792 793
    with_tmp_ref(oldrev) do |tmp_ref|
      # Make commit in tmp ref
      newrev = yield(tmp_ref)

      unless newrev
        raise CommitError.new('Failed to create commit')
      end
794

795
      GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do
796
        if was_empty || !target_branch
797 798
          # Create branch
          rugged.references.create(ref, newrev)
799
        else
800 801 802 803 804 805 806 807 808
          # Update head
          current_head = find_branch(branch).target

          # Make sure target branch was not changed during pre-receive hook
          if current_head == oldrev
            rugged.references.update(ref, newrev)
          else
            raise CommitError.new('Commit was rejected because branch received new push')
          end
809 810
        end
      end
811 812

      newrev
813 814 815
    end
  end

816 817 818 819 820
  def ls_files(ref)
    actual_ref = ref || root_ref
    raw_repository.ls_files(actual_ref)
  end

821 822
  private

823 824 825
  def cache
    @cache ||= RepositoryCache.new(path_with_namespace)
  end
826
end