1# frozen_string_literal: true 2 3require 'securerandom' 4 5class Repository 6 REF_MERGE_REQUEST = 'merge-requests' 7 REF_KEEP_AROUND = 'keep-around' 8 REF_ENVIRONMENTS = 'environments' 9 REF_PIPELINES = 'pipelines' 10 11 ARCHIVE_CACHE_TIME = 60 # Cache archives referred to by a (mutable) ref for 1 minute 12 ARCHIVE_CACHE_TIME_IMMUTABLE = 3600 # Cache archives referred to by an immutable reference for 1 hour 13 14 RESERVED_REFS_NAMES = %W[ 15 heads 16 tags 17 replace 18 #{REF_ENVIRONMENTS} 19 #{REF_KEEP_AROUND} 20 #{REF_PIPELINES} 21 ].freeze 22 23 include Gitlab::RepositoryCacheAdapter 24 25 attr_accessor :full_path, :shard, :disk_path, :container, :repo_type 26 27 delegate :lfs_enabled?, to: :container 28 29 delegate_missing_to :raw_repository 30 31 CreateTreeError = Class.new(StandardError) 32 AmbiguousRefError = Class.new(StandardError) 33 34 # Methods that cache data from the Git repository. 35 # 36 # Each entry in this Array should have a corresponding method with the exact 37 # same name. The cache key used by those methods must also match method's 38 # name. 39 # 40 # For example, for entry `:commit_count` there's a method called `commit_count` which 41 # stores its data in the `commit_count` cache key. 42 CACHED_METHODS = %i(size commit_count readme_path contribution_guide 43 changelog license_blob license_key gitignore 44 gitlab_ci_yml branch_names tag_names branch_count 45 tag_count avatar exists? root_ref merged_branch_names 46 has_visible_content? issue_template_names_hash merge_request_template_names_hash 47 user_defined_metrics_dashboard_paths xcode_project? has_ambiguous_refs?).freeze 48 49 # Methods that use cache_method but only memoize the value 50 MEMOIZED_CACHED_METHODS = %i(license).freeze 51 52 # Certain method caches should be refreshed when certain types of files are 53 # changed. This Hash maps file types (as returned by Gitlab::FileDetector) to 54 # the corresponding methods to call for refreshing caches. 55 METHOD_CACHES_FOR_FILE_TYPES = { 56 readme: %i(readme_path), 57 changelog: :changelog, 58 license: %i(license_blob license_key license), 59 contributing: :contribution_guide, 60 gitignore: :gitignore, 61 gitlab_ci: :gitlab_ci_yml, 62 avatar: :avatar, 63 issue_template: :issue_template_names_hash, 64 merge_request_template: :merge_request_template_names_hash, 65 metrics_dashboard: :user_defined_metrics_dashboard_paths, 66 xcode_config: :xcode_project? 67 }.freeze 68 69 def initialize(full_path, container, shard:, disk_path: nil, repo_type: Gitlab::GlRepository::PROJECT) 70 @full_path = full_path 71 @shard = shard 72 @disk_path = disk_path || full_path 73 @container = container 74 @commit_cache = {} 75 @repo_type = repo_type 76 end 77 78 def ==(other) 79 other.is_a?(self.class) && @disk_path == other.disk_path 80 end 81 82 alias_method :eql?, :== 83 84 def hash 85 [self.class, @disk_path].hash 86 end 87 88 def raw_repository 89 return unless full_path 90 91 @raw_repository ||= initialize_raw_repository 92 end 93 94 alias_method :raw, :raw_repository 95 96 # Don't use this! It's going away. Use Gitaly to read or write from repos. 97 def path_to_repo 98 @path_to_repo ||= 99 begin 100 storage = Gitlab.config.repositories.storages[shard] 101 102 File.expand_path( 103 File.join(storage.legacy_disk_path, disk_path + '.git') 104 ) 105 end 106 end 107 108 def inspect 109 "#<#{self.class.name}:#{@disk_path}>" 110 end 111 112 def commit(ref = nil) 113 return unless exists? 114 return ref if ref.is_a?(::Commit) 115 116 find_commit(ref || root_ref) 117 end 118 119 # Finding a commit by the passed SHA 120 # Also takes care of caching, based on the SHA 121 def commit_by(oid:) 122 return @commit_cache[oid] if @commit_cache.key?(oid) 123 124 @commit_cache[oid] = find_commit(oid) 125 end 126 127 def commits_by(oids:) 128 return [] unless oids.present? 129 130 commits = Gitlab::Git::Commit.batch_by_oid(raw_repository, oids) 131 132 if commits.present? 133 Commit.decorate(commits, container) 134 else 135 [] 136 end 137 end 138 139 def commits(ref = nil, opts = {}) 140 options = { 141 repo: raw_repository, 142 ref: ref, 143 path: opts[:path], 144 author: opts[:author], 145 follow: Array(opts[:path]).length == 1, 146 limit: opts[:limit], 147 offset: opts[:offset], 148 skip_merges: !!opts[:skip_merges], 149 after: opts[:after], 150 before: opts[:before], 151 all: !!opts[:all], 152 first_parent: !!opts[:first_parent], 153 order: opts[:order], 154 literal_pathspec: opts.fetch(:literal_pathspec, true), 155 trailers: opts[:trailers] 156 } 157 158 commits = Gitlab::Git::Commit.where(options) 159 commits = Commit.decorate(commits, container) if commits.present? 160 161 CommitCollection.new(container, commits, ref) 162 end 163 164 def commits_between(from, to, limit: nil) 165 commits = Gitlab::Git::Commit.between(raw_repository, from, to, limit: limit) 166 commits = Commit.decorate(commits, container) if commits.present? 167 commits 168 end 169 170 # Returns a list of commits that are not present in any reference 171 def new_commits(newrev, allow_quarantine: false) 172 commits = raw.new_commits(newrev, allow_quarantine: allow_quarantine) 173 174 ::Commit.decorate(commits, container) 175 end 176 177 # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/384 178 def find_commits_by_message(query, ref = nil, path = nil, limit = 1000, offset = 0) 179 unless exists? && has_visible_content? && query.present? 180 return [] 181 end 182 183 commits = raw_repository.find_commits_by_message(query, ref, path, limit, offset).map do |c| 184 commit(c) 185 end 186 CommitCollection.new(container, commits, ref) 187 end 188 189 def find_branch(name) 190 raw_repository.find_branch(name) 191 end 192 193 def find_tag(name) 194 if @tags.blank? && Feature.enabled?(:find_tag_via_gitaly, project, default_enabled: :yaml) 195 raw_repository.find_tag(name) 196 else 197 tags.find { |tag| tag.name == name } 198 end 199 end 200 201 def ambiguous_ref?(ref) 202 tag_exists?(ref) && branch_exists?(ref) 203 end 204 205 # It's possible for a tag name to be a prefix (including slash) of a branch 206 # name, or vice versa. For instance, a tag named `foo` means we can't create a 207 # tag `foo/bar`, but we _can_ create a branch `foo/bar`. 208 # 209 # If we know a repository has no refs of this type (which is the common case) 210 # then separating refs from paths - as in ExtractsRef - can be faster. 211 # 212 # This method only checks one level deep, so only prefixes that contain no 213 # slashes are considered. If a repository has a tag `foo/bar` and a branch 214 # `foo/bar/baz`, it will return false. 215 def has_ambiguous_refs? 216 return false unless branch_names.present? && tag_names.present? 217 218 with_slash, no_slash = (branch_names + tag_names).partition { |ref| ref.include?('/') } 219 220 return false if with_slash.empty? 221 222 prefixes = no_slash.map { |ref| Regexp.escape(ref) }.join('|') 223 prefix_regex = %r{^(#{prefixes})/} 224 225 with_slash.any? do |ref| 226 prefix_regex.match?(ref) 227 end 228 end 229 cache_method :has_ambiguous_refs? 230 231 def expand_ref(ref) 232 if tag_exists?(ref) 233 Gitlab::Git::TAG_REF_PREFIX + ref 234 elsif branch_exists?(ref) 235 Gitlab::Git::BRANCH_REF_PREFIX + ref 236 end 237 end 238 239 def add_branch(user, branch_name, ref) 240 branch = raw_repository.add_branch(branch_name, user: user, target: ref) 241 242 after_create_branch 243 244 branch 245 rescue Gitlab::Git::Repository::InvalidRef 246 false 247 end 248 249 def add_tag(user, tag_name, target, message = nil) 250 raw_repository.add_tag(tag_name, user: user, target: target, message: message) 251 rescue Gitlab::Git::Repository::InvalidRef 252 false 253 end 254 255 def rm_branch(user, branch_name) 256 before_remove_branch 257 258 raw_repository.rm_branch(branch_name, user: user) 259 260 after_remove_branch 261 true 262 end 263 264 def rm_tag(user, tag_name) 265 before_remove_tag 266 267 raw_repository.rm_tag(tag_name, user: user) 268 269 after_remove_tag 270 true 271 end 272 273 def ref_names 274 branch_names + tag_names 275 end 276 277 def branch_exists?(branch_name) 278 return false unless raw_repository 279 280 branch_names_include?(branch_name) 281 end 282 283 def tag_exists?(tag_name) 284 return false unless raw_repository 285 286 tag_names_include?(tag_name) 287 end 288 289 def ref_exists?(ref) 290 !!raw_repository&.ref_exists?(ref) 291 rescue ArgumentError 292 false 293 end 294 295 def search_branch_names(pattern) 296 redis_set_cache.search('branch_names', pattern) { branch_names } 297 end 298 299 def languages 300 return [] if empty? 301 302 raw_repository.languages(root_ref) 303 end 304 305 def keep_around(*shas) 306 Gitlab::Git::KeepAround.execute(self, shas) 307 end 308 309 def archive_metadata(ref, storage_path, format = "tar.gz", append_sha:, path: nil) 310 raw_repository.archive_metadata( 311 ref, 312 storage_path, 313 project&.path, 314 format, 315 append_sha: append_sha, 316 path: path 317 ) 318 end 319 320 def cached_methods 321 CACHED_METHODS 322 end 323 324 def expire_tags_cache 325 expire_method_caches(%i(tag_names tag_count has_ambiguous_refs?)) 326 @tags = nil 327 @tag_names_include = nil 328 end 329 330 def expire_branches_cache 331 expire_method_caches(%i(branch_names merged_branch_names branch_count has_visible_content? has_ambiguous_refs?)) 332 @local_branches = nil 333 @branch_exists_memo = nil 334 @branch_names_include = nil 335 end 336 337 def expire_statistics_caches 338 expire_method_caches(%i(size commit_count)) 339 end 340 341 def expire_all_method_caches 342 expire_method_caches(CACHED_METHODS) 343 end 344 345 def expire_avatar_cache 346 expire_method_caches(%i(avatar)) 347 end 348 349 # Refreshes the method caches of this repository. 350 # 351 # types - An Array of file types (e.g. `:readme`) used to refresh extra 352 # caches. 353 def refresh_method_caches(types) 354 return if types.empty? 355 356 to_refresh = [] 357 358 types.each do |type| 359 methods = METHOD_CACHES_FOR_FILE_TYPES[type.to_sym] 360 361 to_refresh.concat(Array(methods)) if methods 362 end 363 364 expire_method_caches(to_refresh) 365 366 to_refresh.each { |method| send(method) } # rubocop:disable GitlabSecurity/PublicSend 367 end 368 369 def expire_branch_cache(branch_name = nil) 370 # When we push to the root branch we have to flush the cache for all other 371 # branches as their statistics are based on the commits relative to the 372 # root branch. 373 if !branch_name || branch_name == root_ref 374 branches.each do |branch| 375 cache.expire(:"diverging_commit_counts_#{branch.name}") 376 cache.expire(:"commit_count_#{branch.name}") 377 end 378 # In case a commit is pushed to a non-root branch we only have to flush the 379 # cache for said branch. 380 else 381 cache.expire(:"diverging_commit_counts_#{branch_name}") 382 cache.expire(:"commit_count_#{branch_name}") 383 end 384 end 385 386 def expire_root_ref_cache 387 expire_method_caches(%i(root_ref)) 388 end 389 390 # Expires the cache(s) used to determine if a repository is empty or not. 391 def expire_emptiness_caches 392 return unless empty? 393 394 expire_method_caches(%i(has_visible_content?)) 395 raw_repository.expire_has_local_branches_cache 396 end 397 398 def expire_exists_cache 399 expire_method_caches(%i(exists?)) 400 end 401 402 # expire cache that doesn't depend on repository data (when expiring) 403 def expire_content_cache 404 expire_tags_cache 405 expire_branches_cache 406 expire_root_ref_cache 407 expire_emptiness_caches 408 expire_exists_cache 409 expire_statistics_caches 410 end 411 412 def expire_status_cache 413 expire_exists_cache 414 expire_root_ref_cache 415 expire_emptiness_caches 416 end 417 418 # Runs code after a repository has been created. 419 def after_create 420 expire_status_cache 421 422 repository_event(:create_repository) 423 end 424 425 # Runs code just before a repository is deleted. 426 def before_delete 427 expire_exists_cache 428 expire_all_method_caches 429 expire_branch_cache if exists? 430 expire_content_cache 431 432 repository_event(:remove_repository) 433 end 434 435 # Runs code just before the HEAD of a repository is changed. 436 def before_change_head 437 # Cached divergent commit counts are based on repository head 438 expire_branch_cache 439 expire_root_ref_cache 440 441 repository_event(:change_default_branch) 442 end 443 444 # Runs code before pushing (= creating or removing) a tag. 445 # 446 # Note that this doesn't expire the tags. You may need to call 447 # expire_caches_for_tags or expire_tags_cache. 448 def before_push_tag 449 repository_event(:push_tag) 450 end 451 452 def expire_caches_for_tags 453 expire_statistics_caches 454 expire_emptiness_caches 455 expire_tags_cache 456 end 457 458 # Runs code before removing a tag. 459 def before_remove_tag 460 expire_caches_for_tags 461 462 repository_event(:remove_tag) 463 end 464 465 # Runs code after removing a tag. 466 def after_remove_tag 467 expire_caches_for_tags 468 end 469 470 # Runs code after the HEAD of a repository is changed. 471 def after_change_head 472 expire_all_method_caches 473 container.after_repository_change_head 474 end 475 476 # Runs code after a new commit has been pushed. 477 def after_push_commit(branch_name) 478 expire_statistics_caches 479 expire_branch_cache(branch_name) 480 481 repository_event(:push_commit, branch: branch_name) 482 end 483 484 # Runs code after a new branch has been created. 485 def after_create_branch(expire_cache: true) 486 expire_branches_cache if expire_cache 487 488 repository_event(:push_branch) 489 end 490 491 # Runs code before removing an existing branch. 492 def before_remove_branch 493 expire_branches_cache 494 495 repository_event(:remove_branch) 496 end 497 498 # Runs code after an existing branch has been removed. 499 def after_remove_branch(expire_cache: true) 500 expire_branches_cache if expire_cache 501 end 502 503 def lookup(sha) 504 strong_memoize("lookup_#{sha}") do 505 raw_repository.lookup(sha) 506 end 507 end 508 509 def blob_at(sha, path, limit: Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE) 510 Blob.decorate(raw_repository.blob_at(sha, path, limit: limit), container) 511 rescue Gitlab::Git::Repository::NoRepository 512 nil 513 end 514 515 # items is an Array like: [[oid, path], [oid1, path1]] 516 def blobs_at(items, blob_size_limit: Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE) 517 return [] unless exists? 518 519 raw_repository.batch_blobs(items, blob_size_limit: blob_size_limit).map do |blob| 520 Blob.decorate(blob, container) 521 end 522 rescue Gitlab::Git::Repository::NoRepository 523 [] 524 end 525 526 def root_ref 527 raw_repository&.root_ref 528 end 529 cache_method_asymmetrically :root_ref 530 531 # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/314 532 def exists? 533 return false unless full_path 534 535 raw_repository.exists? 536 end 537 cache_method_asymmetrically :exists? 538 539 # We don't need to cache the output of this method because both exists? and 540 # has_visible_content? are already memoized and cached. There's no guarantee 541 # that the values are expired and loaded atomically. 542 def empty? 543 return true unless exists? 544 545 !has_visible_content? 546 end 547 548 # The size of this repository in megabytes. 549 def size 550 exists? ? raw_repository.size : 0.0 551 end 552 cache_method :size, fallback: 0.0 553 554 def commit_count 555 root_ref ? raw_repository.commit_count(root_ref) : 0 556 end 557 cache_method :commit_count, fallback: 0 558 559 def commit_count_for_ref(ref) 560 return 0 unless exists? 561 562 cache.fetch(:"commit_count_#{ref}") { raw_repository.commit_count(ref) } 563 end 564 565 delegate :branch_names, to: :raw_repository 566 cache_method_as_redis_set :branch_names, fallback: [] 567 568 delegate :tag_names, to: :raw_repository 569 cache_method_as_redis_set :tag_names, fallback: [] 570 571 delegate :branch_count, :tag_count, :has_visible_content?, to: :raw_repository 572 cache_method :branch_count, fallback: 0 573 cache_method :tag_count, fallback: 0 574 cache_method_asymmetrically :has_visible_content? 575 576 def avatar 577 # n+1: https://gitlab.com/gitlab-org/gitlab-foss/issues/38327 578 Gitlab::GitalyClient.allow_n_plus_1_calls do 579 if tree = file_on_head(:avatar) 580 tree.path 581 end 582 end 583 end 584 cache_method :avatar 585 586 # store issue_template_names as hash 587 def issue_template_names_hash 588 Gitlab::Template::IssueTemplate.repository_template_names(project) 589 end 590 cache_method :issue_template_names_hash, fallback: {} 591 592 def merge_request_template_names_hash 593 Gitlab::Template::MergeRequestTemplate.repository_template_names(project) 594 end 595 cache_method :merge_request_template_names_hash, fallback: {} 596 597 def user_defined_metrics_dashboard_paths 598 Gitlab::Metrics::Dashboard::RepoDashboardFinder.list_dashboards(project) 599 end 600 cache_method :user_defined_metrics_dashboard_paths, fallback: [] 601 602 def readme 603 head_tree&.readme 604 end 605 606 def readme_path 607 head_tree&.readme_path 608 end 609 cache_method :readme_path 610 611 def contribution_guide 612 file_on_head(:contributing) 613 end 614 cache_method :contribution_guide 615 616 def changelog 617 file_on_head(:changelog) 618 end 619 cache_method :changelog 620 621 def license_blob 622 file_on_head(:license) 623 end 624 cache_method :license_blob 625 626 def license_key 627 return unless exists? 628 629 raw_repository.license_short_name 630 end 631 cache_method :license_key 632 633 def license 634 return unless license_key 635 636 licensee_object = Licensee::License.new(license_key) 637 638 return if licensee_object.name.blank? 639 640 licensee_object 641 rescue Licensee::InvalidLicense => ex 642 Gitlab::ErrorTracking.track_exception(ex) 643 nil 644 end 645 memoize_method :license 646 647 def gitignore 648 file_on_head(:gitignore) 649 end 650 cache_method :gitignore 651 652 def gitlab_ci_yml 653 file_on_head(:gitlab_ci) 654 end 655 cache_method :gitlab_ci_yml 656 657 def xcode_project? 658 file_on_head(:xcode_config, :tree).present? 659 end 660 cache_method :xcode_project? 661 662 def head_commit 663 @head_commit ||= commit(self.root_ref) 664 end 665 666 def head_tree 667 if head_commit 668 @head_tree ||= Tree.new(self, head_commit.sha, nil) 669 end 670 end 671 672 def tree(sha = :head, path = nil, recursive: false, pagination_params: nil) 673 if sha == :head 674 return unless head_commit 675 676 if path.nil? 677 return head_tree 678 else 679 sha = head_commit.sha 680 end 681 end 682 683 Tree.new(self, sha, path, recursive: recursive, pagination_params: pagination_params) 684 end 685 686 def blob_at_branch(branch_name, path) 687 last_commit = commit(branch_name) 688 689 if last_commit 690 blob_at(last_commit.sha, path) 691 else 692 nil 693 end 694 end 695 696 def list_last_commits_for_tree(sha, path, offset: 0, limit: 25, literal_pathspec: false) 697 commits = raw_repository.list_last_commits_for_tree(sha, path, offset: offset, limit: limit, literal_pathspec: literal_pathspec) 698 699 commits.each do |path, commit| 700 commits[path] = ::Commit.new(commit, container) 701 end 702 end 703 704 def last_commit_for_path(sha, path, literal_pathspec: false) 705 commit = raw_repository.last_commit_for_path(sha, path, literal_pathspec: literal_pathspec) 706 ::Commit.new(commit, container) if commit 707 end 708 709 def last_commit_id_for_path(sha, path, literal_pathspec: false) 710 key = path.blank? ? "last_commit_id_for_path:#{sha}" : "last_commit_id_for_path:#{sha}:#{Digest::SHA1.hexdigest(path)}" 711 712 cache.fetch(key) do 713 last_commit_for_path(sha, path, literal_pathspec: literal_pathspec)&.id 714 end 715 end 716 717 def next_branch(name, opts = {}) 718 branch_ids = self.branch_names.map do |n| 719 next 1 if n == name 720 721 result = n.match(/\A#{name}-([0-9]+)\z/) 722 result[1].to_i if result 723 end.compact 724 725 highest_branch_id = branch_ids.max || 0 726 727 return name if opts[:mild] && 0 == highest_branch_id 728 729 "#{name}-#{highest_branch_id + 1}" 730 end 731 732 def branches_sorted_by(sort_by, pagination_params = nil) 733 raw_repository.local_branches(sort_by: sort_by, pagination_params: pagination_params) 734 end 735 736 def tags_sorted_by(value, pagination_params = nil) 737 raw_repository.tags(sort_by: value, pagination_params: pagination_params) 738 end 739 740 # Params: 741 # 742 # order_by: name|email|commits 743 # sort: asc|desc default: 'asc' 744 def contributors(order_by: nil, sort: 'asc') 745 commits = self.commits(nil, limit: 2000, offset: 0, skip_merges: true) 746 747 commits = commits.group_by(&:author_email).map do |email, commits| 748 contributor = Gitlab::Contributor.new 749 contributor.email = email 750 751 commits.each do |commit| 752 if contributor.name.blank? 753 contributor.name = commit.author_name 754 end 755 756 contributor.commits += 1 757 end 758 759 contributor 760 end 761 Commit.order_by(collection: commits, order_by: order_by, sort: sort) 762 end 763 764 def branch_names_contains(sha) 765 raw_repository.branch_names_contains_sha(sha) 766 end 767 768 def tag_names_contains(sha) 769 raw_repository.tag_names_contains_sha(sha) 770 end 771 772 def local_branches 773 @local_branches ||= raw_repository.local_branches 774 end 775 776 alias_method :branches, :local_branches 777 778 def tags 779 @tags ||= raw_repository.tags 780 end 781 782 def create_dir(user, path, **options) 783 options[:actions] = [{ action: :create_dir, file_path: path }] 784 785 multi_action(user, **options) 786 end 787 788 def create_file(user, path, content, **options) 789 options[:actions] = [{ action: :create, file_path: path, content: content }] 790 791 multi_action(user, **options) 792 end 793 794 def update_file(user, path, content, **options) 795 previous_path = options.delete(:previous_path) 796 action = previous_path && previous_path != path ? :move : :update 797 798 options[:actions] = [{ action: action, file_path: path, previous_path: previous_path, content: content }] 799 800 multi_action(user, **options) 801 end 802 803 def delete_file(user, path, **options) 804 options[:actions] = [{ action: :delete, file_path: path }] 805 806 multi_action(user, **options) 807 end 808 809 def with_cache_hooks 810 result = yield 811 812 return unless result 813 814 after_create if result.repo_created? 815 after_create_branch if result.branch_created? 816 817 result.newrev 818 end 819 820 def multi_action(user, **options) 821 start_project = options.delete(:start_project) 822 823 if start_project 824 options[:start_repository] = start_project.repository.raw_repository 825 end 826 827 with_cache_hooks { raw.multi_action(user, **options) } 828 end 829 830 def merge(user, source_sha, merge_request, message) 831 with_cache_hooks do 832 raw_repository.merge(user, source_sha, merge_request.target_branch, message) do |commit_id| 833 merge_request.update_and_mark_in_progress_merge_commit_sha(commit_id) 834 nil # Return value does not matter. 835 end 836 end 837 end 838 839 def delete_refs(*ref_names) 840 raw.delete_refs(*ref_names) 841 end 842 843 def ff_merge(user, source, target_branch, merge_request: nil) 844 their_commit_id = commit(source)&.id 845 raise 'Invalid merge source' if their_commit_id.nil? 846 847 merge_request&.update_and_mark_in_progress_merge_commit_sha(their_commit_id) 848 849 with_cache_hooks { raw.ff_merge(user, their_commit_id, target_branch) } 850 end 851 852 def revert( 853 user, commit, branch_name, message, 854 start_branch_name: nil, start_project: project, dry_run: false) 855 856 with_cache_hooks do 857 raw_repository.revert( 858 user: user, 859 commit: commit.raw, 860 branch_name: branch_name, 861 message: message, 862 start_branch_name: start_branch_name, 863 start_repository: start_project.repository.raw_repository, 864 dry_run: dry_run 865 ) 866 end 867 end 868 869 def cherry_pick( 870 user, commit, branch_name, message, 871 start_branch_name: nil, start_project: project, dry_run: false) 872 873 with_cache_hooks do 874 raw_repository.cherry_pick( 875 user: user, 876 commit: commit.raw, 877 branch_name: branch_name, 878 message: message, 879 start_branch_name: start_branch_name, 880 start_repository: start_project.repository.raw_repository, 881 dry_run: dry_run 882 ) 883 end 884 end 885 886 def merged_to_root_ref?(branch_or_name) 887 branch = Gitlab::Git::Branch.find(self, branch_or_name) 888 889 if branch 890 same_head = branch.target == root_ref_sha 891 merged = ancestor?(branch.target, root_ref_sha) 892 !same_head && merged 893 else 894 nil 895 end 896 end 897 898 def root_ref_sha 899 @root_ref_sha ||= commit(root_ref).sha 900 end 901 902 # If this method is not provided a set of branch names to check merge status, 903 # it fetches all branches. 904 def merged_branch_names(branch_names = []) 905 # Currently we should skip caching if requesting all branch names 906 # This is only used in a few places, notably app/services/branches/delete_merged_service.rb, 907 # and it could potentially result in a very large cache. 908 return raw_repository.merged_branch_names(branch_names) if branch_names.empty? 909 910 cache = redis_hash_cache 911 912 merged_branch_names_hash = cache.fetch_and_add_missing(:merged_branch_names, branch_names) do |missing_branch_names, hash| 913 merged = raw_repository.merged_branch_names(missing_branch_names) 914 915 missing_branch_names.each do |bn| 916 # Redis only stores strings in hset keys, use a fancy encoder 917 hash[bn] = Gitlab::Redis::Boolean.new(merged.include?(bn)) 918 end 919 end 920 921 Set.new(merged_branch_names_hash.select { |_, v| Gitlab::Redis::Boolean.true?(v) }.keys) 922 end 923 924 def merge_base(*commits_or_ids) 925 commit_ids = commits_or_ids.map do |commit_or_id| 926 commit_or_id.is_a?(::Commit) ? commit_or_id.id : commit_or_id 927 end 928 929 raw_repository.merge_base(*commit_ids) 930 end 931 932 def ancestor?(ancestor_id, descendant_id) 933 return false if ancestor_id.nil? || descendant_id.nil? 934 935 cache_key = "ancestor:#{ancestor_id}:#{descendant_id}" 936 request_store_cache.fetch(cache_key) do 937 cache.fetch(cache_key) do 938 raw_repository.ancestor?(ancestor_id, descendant_id) 939 end 940 end 941 end 942 943 def fetch_as_mirror(url, forced: false, refmap: :all_refs, prune: true, http_authorization_header: "") 944 fetch_remote(url, refmap: refmap, forced: forced, prune: prune, http_authorization_header: http_authorization_header) 945 end 946 947 def fetch_source_branch!(source_repository, source_branch, local_ref) 948 raw_repository.fetch_source_branch!(source_repository.raw_repository, source_branch, local_ref) 949 end 950 951 def compare_source_branch(target_branch_name, source_repository, source_branch_name, straight:) 952 raw_repository.compare_source_branch(target_branch_name, source_repository.raw_repository, source_branch_name, straight: straight) 953 end 954 955 def create_ref(ref, ref_path) 956 raw_repository.write_ref(ref_path, ref) 957 end 958 959 def ls_files(ref) 960 actual_ref = ref || root_ref 961 raw_repository.ls_files(actual_ref) 962 end 963 964 def search_files_by_content(query, ref, options = {}) 965 return [] if empty? || query.blank? 966 967 raw_repository.search_files_by_content(query, ref, options) 968 end 969 970 def search_files_by_name(query, ref) 971 return [] if empty? 972 973 raw_repository.search_files_by_name(query, ref) 974 end 975 976 def search_files_by_wildcard_path(path, ref = 'HEAD') 977 # We need to use RE2 to match Gitaly's regexp engine 978 regexp_string = RE2::Regexp.escape(path) 979 980 anything = '.*?' 981 anything_but_not_slash = '([^\/])*?' 982 regexp_string.gsub!('\*\*', anything) 983 regexp_string.gsub!('\*', anything_but_not_slash) 984 985 raw_repository.search_files_by_regexp("^#{regexp_string}$", ref) 986 end 987 988 def copy_gitattributes(ref) 989 actual_ref = ref || root_ref 990 begin 991 raw_repository.copy_gitattributes(actual_ref) 992 true 993 rescue Gitlab::Git::Repository::InvalidRef 994 false 995 end 996 end 997 998 def file_on_head(type, object_type = :blob) 999 return unless head = tree(:head) 1000 1001 objects = 1002 case object_type 1003 when :blob 1004 head.blobs 1005 when :tree 1006 head.trees 1007 else 1008 raise ArgumentError, "Object type #{object_type} is not supported" 1009 end 1010 1011 objects.find do |object| 1012 Gitlab::FileDetector.type_of(object.path) == type 1013 end 1014 end 1015 1016 def route_map_for(sha) 1017 blob_data_at(sha, '.gitlab/route-map.yml') 1018 end 1019 1020 def gitlab_ci_yml_for(sha, path = '.gitlab-ci.yml') 1021 blob_data_at(sha, path) 1022 end 1023 1024 def lfsconfig_for(sha) 1025 blob_data_at(sha, '.lfsconfig') 1026 end 1027 1028 def changelog_config(ref = 'HEAD') 1029 blob_data_at(ref, Gitlab::Changelog::Config::FILE_PATH) 1030 end 1031 1032 def fetch_ref(source_repository, source_ref:, target_ref:) 1033 raw_repository.fetch_ref(source_repository.raw_repository, source_ref: source_ref, target_ref: target_ref) 1034 end 1035 1036 def rebase(user, merge_request, skip_ci: false) 1037 push_options = [] 1038 push_options << Gitlab::PushOptions::CI_SKIP if skip_ci 1039 1040 raw.rebase( 1041 user, 1042 merge_request.id, 1043 branch: merge_request.source_branch, 1044 branch_sha: merge_request.source_branch_sha, 1045 remote_repository: merge_request.target_project.repository.raw, 1046 remote_branch: merge_request.target_branch, 1047 push_options: push_options 1048 ) do |commit_id| 1049 merge_request.update!(rebase_commit_sha: commit_id, merge_error: nil) 1050 end 1051 rescue StandardError => error 1052 merge_request.update!(rebase_commit_sha: nil) 1053 raise error 1054 end 1055 1056 def squash(user, merge_request, message) 1057 raw.squash(user, start_sha: merge_request.diff_start_sha, 1058 end_sha: merge_request.diff_head_sha, 1059 author: merge_request.author, 1060 message: message) 1061 end 1062 1063 def submodule_links 1064 @submodule_links ||= ::Gitlab::SubmoduleLinks.new(self) 1065 end 1066 1067 def update_submodule(user, submodule, commit_sha, message:, branch:) 1068 with_cache_hooks do 1069 raw.update_submodule( 1070 user: user, 1071 submodule: submodule, 1072 commit_sha: commit_sha, 1073 branch: branch, 1074 message: message 1075 ) 1076 end 1077 end 1078 1079 def blob_data_at(sha, path) 1080 blob = blob_at(sha, path) 1081 return unless blob 1082 1083 blob.load_all_data! 1084 blob.data 1085 end 1086 1087 def create_if_not_exists 1088 return if exists? 1089 1090 raw.create_repository 1091 after_create 1092 1093 true 1094 rescue Gitlab::Git::Repository::RepositoryExists 1095 # We do not want to call `#after_create` given that we didn't create the 1096 # repo, but we obviously have a mismatch between what's in our exists cache 1097 # and actual on-disk state as seen by Gitaly. Let's thus expire our caches. 1098 expire_status_cache 1099 1100 nil 1101 end 1102 1103 def create_from_bundle(bundle_path) 1104 raw.create_from_bundle(bundle_path).tap do |result| 1105 after_create if result 1106 end 1107 end 1108 1109 def blobs_metadata(paths, ref = 'HEAD') 1110 references = Array.wrap(paths).map { |path| [ref, path] } 1111 1112 Gitlab::Git::Blob.batch_metadata(raw, references).map { |raw_blob| Blob.decorate(raw_blob) } 1113 end 1114 1115 def project 1116 if container.is_a?(Project) 1117 container 1118 else 1119 container.try(:project) 1120 end 1121 end 1122 1123 # Choose one of the available repository storage options based on a normalized weighted probability. 1124 # We should always use the latest settings, to avoid picking a deleted shard. 1125 def self.pick_storage_shard(expire: true) 1126 Gitlab::CurrentSettings.expire_current_application_settings if expire 1127 Gitlab::CurrentSettings.pick_repository_storage 1128 end 1129 1130 def change_head(branch) 1131 if branch_exists?(branch) 1132 before_change_head 1133 raw_repository.write_ref('HEAD', "refs/heads/#{branch}") 1134 copy_gitattributes(branch) 1135 after_change_head 1136 else 1137 container.after_change_head_branch_does_not_exist(branch) 1138 1139 false 1140 end 1141 end 1142 1143 def cache 1144 @cache ||= Gitlab::RepositoryCache.new(self) 1145 end 1146 1147 private 1148 1149 # TODO Genericize finder, later split this on finders by Ref or Oid 1150 # https://gitlab.com/gitlab-org/gitlab/issues/19877 1151 def find_commit(oid_or_ref) 1152 commit = if oid_or_ref.is_a?(Gitlab::Git::Commit) 1153 oid_or_ref 1154 else 1155 Gitlab::Git::Commit.find(raw_repository, oid_or_ref) 1156 end 1157 1158 ::Commit.new(commit, container) if commit 1159 end 1160 1161 def redis_set_cache 1162 @redis_set_cache ||= Gitlab::RepositorySetCache.new(self) 1163 end 1164 1165 def redis_hash_cache 1166 @redis_hash_cache ||= Gitlab::RepositoryHashCache.new(self) 1167 end 1168 1169 def request_store_cache 1170 @request_store_cache ||= Gitlab::RepositoryCache.new(self, backend: Gitlab::SafeRequestStore) 1171 end 1172 1173 def repository_event(event, tags = {}) 1174 Gitlab::Metrics.add_event(event, tags) 1175 end 1176 1177 def initialize_raw_repository 1178 Gitlab::Git::Repository.new(shard, 1179 disk_path + '.git', 1180 repo_type.identifier_for_container(container), 1181 container.full_path) 1182 end 1183end 1184 1185Repository.prepend_mod_with('Repository') 1186