diff options
Diffstat (limited to 'app/models/repository.rb')
-rw-r--r-- | app/models/repository.rb | 199 |
1 files changed, 51 insertions, 148 deletions
diff --git a/app/models/repository.rb b/app/models/repository.rb index 9c879e2006b..1cf55fd4332 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -20,6 +20,7 @@ class Repository attr_accessor :full_path, :disk_path, :project, :is_wiki delegate :ref_name_for_sha, to: :raw_repository + delegate :bundle_to_disk, :create_from_bundle, to: :raw_repository CreateTreeError = Class.new(StandardError) @@ -92,6 +93,10 @@ class Repository alias_method :raw, :raw_repository + def cleanup + @raw_repository&.cleanup + end + # Return absolute path to repository def path_to_repo @path_to_repo ||= File.expand_path( @@ -103,6 +108,10 @@ class Repository "#<#{self.class.name}:#{@disk_path}>" end + def create_hooks + Gitlab::Git::Repository.create_hooks(path_to_repo, Gitlab.config.gitlab_shell.hooks_path) + end + def commit(ref = 'HEAD') return nil unless exists? return ref if ref.is_a?(::Commit) @@ -155,34 +164,27 @@ class Repository commits end + # Returns a list of commits that are not present in any reference + def new_commits(newrev) + refs = ::Gitlab::Git::RevList.new(raw, newrev: newrev).new_refs + + refs.map { |sha| commit(sha.strip) } + end + # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/384 def find_commits_by_message(query, ref = nil, path = nil, limit = 1000, offset = 0) unless exists? && has_visible_content? && query.present? return [] end - raw_repository.gitaly_migrate(:commits_by_message) do |is_enabled| - commits = - if is_enabled - find_commits_by_message_by_gitaly(query, ref, path, limit, offset) - else - find_commits_by_message_by_shelling_out(query, ref, path, limit, offset) - end - - CommitCollection.new(project, commits, ref) + commits = raw_repository.find_commits_by_message(query, ref, path, limit, offset).map do |c| + commit(c) end + CommitCollection.new(project, commits, ref) end def find_branch(name, fresh_repo: true) - # Since the Repository object may have in-memory index changes, invalidating the memoized Repository object may - # cause unintended side effects. Because finding a branch is a read-only operation, we can safely instantiate - # a new repo here to ensure a consistent state to avoid a libgit2 bug where concurrent access (e.g. via git gc) - # may cause the branch to "disappear" erroneously or have the wrong SHA. - # - # See: https://github.com/libgit2/libgit2/issues/1534 and https://gitlab.com/gitlab-org/gitlab-ce/issues/15392 - raw_repo = fresh_repo ? initialize_raw_repository : raw_repository - - raw_repo.find_branch(name) + raw_repository.find_branch(name, fresh_repo) end def find_tag(name) @@ -255,25 +257,15 @@ class Repository return if kept_around?(sha) # This will still fail if the file is corrupted (e.g. 0 bytes) - begin - write_ref(keep_around_ref_name(sha), sha) - rescue Rugged::ReferenceError => ex - Rails.logger.error "Unable to create #{REF_KEEP_AROUND} reference for repository #{path}: #{ex}" - rescue Rugged::OSError => ex - raise unless ex.message =~ /Failed to create locked file/ && ex.message =~ /File exists/ - - Rails.logger.error "Unable to create #{REF_KEEP_AROUND} reference for repository #{path}: #{ex}" - end + raw_repository.write_ref(keep_around_ref_name(sha), sha, shell: false) + rescue Gitlab::Git::CommandError => ex + Rails.logger.error "Unable to create keep-around reference for repository #{path}: #{ex}" end def kept_around?(sha) ref_exists?(keep_around_ref_name(sha)) end - def write_ref(ref_path, sha) - rugged.references.create(ref_path, sha, force: true) - end - def diverging_commit_counts(branch) root_ref_hash = raw_repository.commit(root_ref).id cache.fetch(:"diverging_commit_counts_#{branch.name}") do @@ -504,7 +496,7 @@ class Repository raw_repository.root_ref else # When the repo does not exist we raise this error so no data is cached. - raise Rugged::ReferenceError + raise Gitlab::Git::Repository::NoRepository end end cache_method :root_ref @@ -538,11 +530,7 @@ class Repository def commit_count_for_ref(ref) return 0 unless exists? - begin - cache.fetch(:"commit_count_#{ref}") { raw_repository.commit_count(ref) } - rescue Rugged::ReferenceError - 0 - end + cache.fetch(:"commit_count_#{ref}") { raw_repository.commit_count(ref) } end delegate :branch_names, to: :raw_repository @@ -666,26 +654,14 @@ class Repository end def last_commit_for_path(sha, path) - raw_repository.gitaly_migrate(:last_commit_for_path) do |is_enabled| - if is_enabled - last_commit_for_path_by_gitaly(sha, path) - else - last_commit_for_path_by_rugged(sha, path) - end - end + commit_by(oid: last_commit_id_for_path(sha, path)) end def last_commit_id_for_path(sha, path) key = path.blank? ? "last_commit_id_for_path:#{sha}" : "last_commit_id_for_path:#{sha}:#{Digest::SHA1.hexdigest(path)}" cache.fetch(key) do - raw_repository.gitaly_migrate(:last_commit_for_path) do |is_enabled| - if is_enabled - last_commit_for_path_by_gitaly(sha, path).id - else - last_commit_id_for_path_by_shelling_out(sha, path) - end - end + raw_repository.last_commit_id_for_path(sha, path) end end @@ -747,29 +723,12 @@ class Repository Commit.order_by(collection: commits, order_by: order_by, sort: sort) end - def refs_contains_sha(ref_type, sha) - args = %W(#{ref_type} --contains #{sha}) - names = run_git(args).first - - if names.respond_to?(:split) - names = names.split("\n").map(&:strip) - - names.each do |name| - name.slice! '* ' - end - - names - else - [] - end - end - def branch_names_contains(sha) - refs_contains_sha('branch', sha) + raw_repository.branch_names_contains_sha(sha) end def tag_names_contains(sha) - refs_contains_sha('tag', sha) + raw_repository.tag_names_contains_sha(sha) end def local_branches @@ -830,17 +789,6 @@ class Repository with_cache_hooks { raw.multi_action(user, **options) } 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 - def merge(user, source_sha, merge_request, message) with_cache_hooks do raw_repository.merge(user, source_sha, merge_request.target_branch, message) do |commit_id| @@ -895,52 +843,30 @@ class Repository branch = Gitlab::Git::Branch.find(self, branch_or_name) if branch - @root_ref_sha ||= commit(root_ref).sha - same_head = branch.target == @root_ref_sha - merged = ancestor?(branch.target, @root_ref_sha) + same_head = branch.target == root_ref_sha + merged = ancestor?(branch.target, root_ref_sha) !same_head && merged else nil end end - delegate :merged_branch_names, to: :raw_repository + def root_ref_sha + @root_ref_sha ||= commit(root_ref).sha + end + + delegate :merged_branch_names, :can_be_merged?, to: :raw_repository def merge_base(first_commit_id, second_commit_id) first_commit_id = commit(first_commit_id).try(:id) || first_commit_id second_commit_id = commit(second_commit_id).try(:id) || second_commit_id raw_repository.merge_base(first_commit_id, second_commit_id) - rescue Rugged::ReferenceError - nil end def ancestor?(ancestor_id, descendant_id) return false if ancestor_id.nil? || descendant_id.nil? - Gitlab::GitalyClient.migrate(:is_ancestor) do |is_enabled| - if is_enabled - raw_repository.ancestor?(ancestor_id, descendant_id) - else - rugged_is_ancestor?(ancestor_id, descendant_id) - end - end - end - - def search_files_by_content(query, ref) - return [] if empty? || query.blank? - - offset = 2 - args = %W(grep -i -I -n --before-context #{offset} --after-context #{offset} -E -e #{Regexp.escape(query)} #{ref || root_ref}) - - run_git(args).first.scrub.split(/^--$/) - end - - def search_files_by_name(query, ref) - return [] if empty? || query.blank? - - args = %W(ls-tree --full-tree -r #{ref || root_ref} --name-status | #{Regexp.escape(query)}) - - run_git(args).first.lines.map(&:strip) + raw_repository.ancestor?(ancestor_id, descendant_id) end def fetch_as_mirror(url, forced: false, refmap: :all_refs, remote_name: nil) @@ -976,6 +902,18 @@ class Repository raw_repository.ls_files(actual_ref) end + def search_files_by_content(query, ref) + return [] if empty? || query.blank? + + raw_repository.search_files_by_content(query, ref) + end + + def search_files_by_name(query, ref) + return [] if empty? + + raw_repository.search_files_by_name(query, ref) + end + def copy_gitattributes(ref) actual_ref = ref || root_ref begin @@ -1014,8 +952,9 @@ class Repository else cache.fetch(key, &block) end + instance_variable_set(ivar, value) - rescue Rugged::ReferenceError, Gitlab::Git::Repository::NoRepository + rescue Gitlab::Git::Repository::NoRepository # Even if the above `#exists?` check passes these errors might still # occur (for example because of a non-existing HEAD). We want to # gracefully handle this and not cache anything @@ -1109,43 +1048,7 @@ class Repository Gitlab::Metrics.add_event(event, { path: full_path }.merge(tags)) end - def last_commit_for_path_by_gitaly(sha, path) - c = raw_repository.gitaly_commit_client.last_commit_for_path(sha, path) - commit_by(oid: c) - end - - def last_commit_for_path_by_rugged(sha, path) - sha = last_commit_id_for_path_by_shelling_out(sha, path) - commit_by(oid: sha) - end - - def last_commit_id_for_path_by_shelling_out(sha, path) - args = %W(rev-list --max-count=1 #{sha} -- #{path}) - raw_repository.run_git_with_timeout(args, Gitlab::Git::Popen::FAST_GIT_PROCESS_TIMEOUT).first.strip - end - def initialize_raw_repository Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git', Gitlab::GlRepository.gl_repository(project, is_wiki)) end - - def find_commits_by_message_by_shelling_out(query, ref, path, limit, offset) - ref ||= root_ref - - args = %W( - log #{ref} --pretty=%H --skip #{offset} - --max-count #{limit} --grep=#{query} --regexp-ignore-case - ) - args = args.concat(%W(-- #{path})) if path.present? - - git_log_results = run_git(args).first.lines - - git_log_results.map { |c| commit(c.chomp) }.compact - end - - def find_commits_by_message_by_gitaly(query, ref, path, limit, offset) - raw_repository - .gitaly_commit_client - .commits_by_message(query, revision: ref, path: path, limit: limit, offset: offset) - .map { |c| commit(c) } - end end |