Class: Repository

Inherits:
Object
  • Object
show all
Includes:
Gitlab::RepositoryCacheAdapter
Defined in:
app/models/repository.rb

Direct Known Subclasses

DesignManagement::GitRepository

Constant Summary collapse

REF_MERGE_REQUEST =
'merge-requests'
REF_KEEP_AROUND =
'keep-around'
REF_ENVIRONMENTS =
'environments'
REF_PIPELINES =
'pipelines'
REF_TMP =
'tmp'
REF_WORKLOADS =
'workloads'
ARCHIVE_CACHE_TIME =

Cache archives referred to by a (mutable) ref for 1 minute

60
ARCHIVE_CACHE_TIME_IMMUTABLE =

Cache archives referred to by an immutable reference for 1 hour

3600
RESERVED_REFS_NAMES =
%W[
  heads
  tags
  replace
  #{REF_MERGE_REQUEST}
  #{REF_ENVIRONMENTS}
  #{REF_KEEP_AROUND}
  #{REF_PIPELINES}
].freeze
FORMAT_SHA1 =
'sha1'
FORMAT_SHA256 =
'sha256'
CreateTreeError =
Class.new(StandardError)
AmbiguousRefError =
Class.new(StandardError)
CACHED_METHODS =

Methods that cache data from the Git repository.

Each entry in this Array should have a corresponding method with the exact same name. The cache key used by those methods must also match method’s name.

For example, for entry :commit_count there’s a method called commit_count which stores its data in the commit_count cache key.

%i[size recent_objects_size commit_count readme_path contribution_guide
changelog license_blob license_gitaly gitignore
branch_names tag_names branch_count
tag_count avatar exists? root_ref merged_branch_names
has_visible_content? issue_template_names_hash merge_request_template_names_hash
xcode_project? has_ambiguous_refs?].freeze
METHOD_CACHES_FOR_FILE_TYPES =

Certain method caches should be refreshed when certain types of files are changed. This Hash maps file types (as returned by Gitlab::FileDetector) to the corresponding methods to call for refreshing caches.

{
  readme: %i[readme_path],
  changelog: :changelog,
  license: %i[license_blob license_gitaly],
  contributing: :contribution_guide,
  gitignore: :gitignore,
  avatar: :avatar,
  issue_template: :issue_template_names_hash,
  merge_request_template: :merge_request_template_names_hash,
  xcode_config: :xcode_project?
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Gitlab::RepositoryCacheAdapter

#cache_method_output, #cache_method_output_as_redis_set, #cache_method_output_asymmetrically, #expire_method_caches, #memoize_method_cache_value, #memoize_method_output, #no_repository_fallback

Constructor Details

#initialize(full_path, container, shard:, disk_path: nil, repo_type: Gitlab::GlRepository::PROJECT) ⇒ Repository

Returns a new instance of Repository.



77
78
79
80
81
82
83
84
# File 'app/models/repository.rb', line 77

def initialize(full_path, container, shard:, disk_path: nil, repo_type: Gitlab::GlRepository::PROJECT)
  @full_path = full_path
  @shard = shard
  @disk_path = disk_path || full_path
  @container = container
  @commit_cache = {}
  @repo_type = repo_type
end

Instance Attribute Details

#containerObject

Returns the value of attribute container.



37
38
39
# File 'app/models/repository.rb', line 37

def container
  @container
end

#disk_pathObject

Returns the value of attribute disk_path.



37
38
39
# File 'app/models/repository.rb', line 37

def disk_path
  @disk_path
end

#full_pathObject

Returns the value of attribute full_path.



37
38
39
# File 'app/models/repository.rb', line 37

def full_path
  @full_path
end

#repo_typeObject

Returns the value of attribute repo_type.



37
38
39
# File 'app/models/repository.rb', line 37

def repo_type
  @repo_type
end

#shardObject

Returns the value of attribute shard.



37
38
39
# File 'app/models/repository.rb', line 37

def shard
  @shard
end

Class Method Details

.pick_storage_shard(expire: true) ⇒ Object

Choose one of the available repository storage options based on a normalized weighted probability. We should always use the latest settings, to avoid picking a deleted shard.



1303
1304
1305
1306
# File 'app/models/repository.rb', line 1303

def self.pick_storage_shard(expire: true)
  Gitlab::CurrentSettings.expire_current_application_settings if expire
  Gitlab::CurrentSettings.pick_repository_storage
end

Instance Method Details

#==(other) ⇒ Object Also known as: eql?



86
87
88
# File 'app/models/repository.rb', line 86

def ==(other)
  other.is_a?(self.class) && @disk_path == other.disk_path
end

#add_branch(user, branch_name, ref, expire_cache: true, skip_ci: false, raise_on_invalid_ref: false) ⇒ Object



282
283
284
285
286
287
288
289
290
291
292
# File 'app/models/repository.rb', line 282

def add_branch(user, branch_name, ref, expire_cache: true, skip_ci: false, raise_on_invalid_ref: false)
  branch = raw_repository.add_branch(branch_name, user: user, target: ref, skip_ci: skip_ci)

  after_create_branch(expire_cache: expire_cache)

  branch
rescue Gitlab::Git::Repository::InvalidRef => e
  raise e if raise_on_invalid_ref

  false
end

#add_tag(user, tag_name, target, message = nil) ⇒ Object



294
295
296
297
298
# File 'app/models/repository.rb', line 294

def add_tag(user, tag_name, target, message = nil)
  raw_repository.add_tag(tag_name, user: user, target: target, message: message)
rescue Gitlab::Git::Repository::InvalidRef
  false
end

#after_change_headObject

Runs code after the HEAD of a repository is changed.



561
562
563
564
# File 'app/models/repository.rb', line 561

def after_change_head
  expire_all_method_caches
  container.after_repository_change_head
end

#after_createObject

Runs code after a repository has been created.



507
508
509
510
511
512
513
# File 'app/models/repository.rb', line 507

def after_create
  expire_status_cache

  repository_event(:create_repository)

  container.after_create_repository
end

#after_create_branch(expire_cache: true) ⇒ Object

Runs code after a new branch has been created.



575
576
577
578
579
# File 'app/models/repository.rb', line 575

def after_create_branch(expire_cache: true)
  expire_branches_cache if expire_cache

  repository_event(:push_branch)
end

#after_push_commit(branch_name) ⇒ Object

Runs code after a new commit has been pushed.



567
568
569
570
571
572
# File 'app/models/repository.rb', line 567

def after_push_commit(branch_name)
  expire_statistics_caches
  expire_branch_cache(branch_name)

  repository_event(:push_commit, branch: branch_name)
end

#after_remove_branch(expire_cache: true) ⇒ Object

Runs code after an existing branch has been removed.



589
590
591
592
593
594
# File 'app/models/repository.rb', line 589

def after_remove_branch(expire_cache: true)
  if expire_cache
    expire_branches_cache
    expire_root_ref_cache
  end
end

#after_remove_tagObject

Runs code after removing a tag.



556
557
558
# File 'app/models/repository.rb', line 556

def after_remove_tag
  expire_caches_for_tags
end

#ambiguous_ref?(ref) ⇒ Boolean

Returns:

  • (Boolean)


240
241
242
# File 'app/models/repository.rb', line 240

def ambiguous_ref?(ref)
  tag_exists?(ref) && branch_exists?(ref)
end

#ancestor?(ancestor_id, descendant_id) ⇒ Boolean

Returns:

  • (Boolean)


1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
# File 'app/models/repository.rb', line 1109

def ancestor?(ancestor_id, descendant_id)
  return false if ancestor_id.nil? || descendant_id.nil?

  cache_key = ancestor_cache_key(ancestor_id, descendant_id)
  request_store_cache.fetch(cache_key) do
    cache.fetch(cache_key) do
      raw_repository.ancestor?(ancestor_id, descendant_id)
    end
  end
end

#archive_metadata(ref, storage_path, format = "tar.gz", append_sha:, path: nil) ⇒ Object



393
394
395
396
397
398
399
400
401
402
# File 'app/models/repository.rb', line 393

def (ref, storage_path, format = "tar.gz", append_sha:, path: nil)
  raw_repository.(
    ref,
    storage_path,
    project&.path,
    format,
    append_sha: append_sha,
    path: path
  )
end

#avatarObject



675
676
677
678
679
680
681
682
# File 'app/models/repository.rb', line 675

def avatar
  # n+1: https://gitlab.com/gitlab-org/gitlab-foss/issues/38327
  Gitlab::GitalyClient.allow_n_plus_1_calls do
    if tree = file_on_head(:avatar)
      tree.path
    end
  end
end

#before_change_headObject

Runs code just before the HEAD of a repository is changed.



526
527
528
529
530
531
532
# File 'app/models/repository.rb', line 526

def before_change_head
  # Cached divergent commit counts are based on repository head
  expire_branch_cache
  expire_root_ref_cache

  repository_event(:change_default_branch)
end

#before_deleteObject

Runs code just before a repository is deleted.



516
517
518
519
520
521
522
523
# File 'app/models/repository.rb', line 516

def before_delete
  expire_exists_cache
  expire_all_method_caches
  expire_branch_cache if exists?
  expire_content_cache

  repository_event(:remove_repository)
end

#before_push_tagObject

Runs code before pushing (= creating or removing) a tag.

Note that this doesn’t expire the tags. You may need to call expire_caches_for_tags or expire_tags_cache.



538
539
540
# File 'app/models/repository.rb', line 538

def before_push_tag
  repository_event(:push_tag)
end

#before_remove_branchObject

Runs code before removing an existing branch.



582
583
584
585
586
# File 'app/models/repository.rb', line 582

def before_remove_branch
  expire_branches_cache

  repository_event(:remove_branch)
end

#before_remove_tagObject

Runs code before removing a tag.



549
550
551
552
553
# File 'app/models/repository.rb', line 549

def before_remove_tag
  expire_caches_for_tags

  repository_event(:remove_tag)
end

#blank_refObject



1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
# File 'app/models/repository.rb', line 1397

def blank_ref
  return Gitlab::Git::SHA1_BLANK_SHA unless exists?

  case object_format
  when FORMAT_SHA1
    Gitlab::Git::SHA1_BLANK_SHA
  when FORMAT_SHA256
    Gitlab::Git::SHA256_BLANK_SHA
  end
end

#blob_at(sha, path, limit: Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE) ⇒ Object



602
603
604
605
606
# File 'app/models/repository.rb', line 602

def blob_at(sha, path, limit: Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE)
  Blob.decorate(raw_repository.blob_at(sha, path, limit: limit), container)
rescue Gitlab::Git::Repository::NoRepository
  nil
end

#blob_at_branch(branch_name, path) ⇒ Object



774
775
776
777
778
779
780
# File 'app/models/repository.rb', line 774

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

  if last_commit
    blob_at(last_commit.sha, path)
  end
end

#blob_data_at(sha, path) ⇒ Object



1263
1264
1265
1266
1267
1268
1269
# File 'app/models/repository.rb', line 1263

def blob_data_at(sha, path)
  blob = blob_at(sha, path)
  return unless blob

  blob.load_all_data!
  blob.data
end

#blobs_at(items, blob_size_limit: Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE) ⇒ Object

items is an Array like: [[oid, path], [oid1, path1]]



609
610
611
612
613
614
615
616
617
# File 'app/models/repository.rb', line 609

def blobs_at(items, blob_size_limit: Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE)
  return [] unless exists?

  raw_repository.batch_blobs(items, blob_size_limit: blob_size_limit).map do |blob|
    Blob.decorate(blob, container)
  end
rescue Gitlab::Git::Repository::NoRepository
  []
end

#branch_exists?(branch_name) ⇒ Boolean

Returns:

  • (Boolean)


334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
# File 'app/models/repository.rb', line 334

def branch_exists?(branch_name)
  return false unless raw_repository

  if Feature.enabled?(:ref_existence_check_gitaly, project)
    return false unless exists?
    return false if branch_name.blank?

    # Optimization: Use a root_ref cache to check the presence of the default branch
    return true if branch_name == root_ref

    Gitlab::Git::RefPreloader.preload_refs_for_project(project)

    return lazy_ref_exists?(Gitlab::Git::BRANCH_REF_PREFIX + branch_name).itself
  end

  branch_names_include?(branch_name)
end

#branch_names_contains(sha, limit: 0, exclude_refs: []) ⇒ Object



860
861
862
863
864
# File 'app/models/repository.rb', line 860

def branch_names_contains(sha, limit: 0, exclude_refs: [])
  refs = raw_repository.branch_names_contains_sha(sha, limit: adjust_containing_limit(limit: limit, exclude_refs: exclude_refs))

  adjust_containing_refs(limit: limit, refs: refs - exclude_refs)
end

#branch_or_tag?(ref) ⇒ Boolean

Returns:

  • (Boolean)


371
372
373
374
375
376
377
# File 'app/models/repository.rb', line 371

def branch_or_tag?(ref)
  return false unless exists?

  ref = Gitlab::Git.ref_name(ref, types: 'heads|tags')

  branch_exists?(ref) || tag_exists?(ref)
end

#branches_sorted_by(sort_by, pagination_params = nil) ⇒ Object



816
817
818
# File 'app/models/repository.rb', line 816

def branches_sorted_by(sort_by, pagination_params = nil)
  raw_repository.local_branches(sort_by: sort_by, pagination_params: pagination_params)
end

#cacheObject



1320
1321
1322
# File 'app/models/repository.rb', line 1320

def cache
  @cache ||= Gitlab::RepositoryCache.new(self)
end

#cached_methodsObject



404
405
406
# File 'app/models/repository.rb', line 404

def cached_methods
  CACHED_METHODS
end

#change_head(branch) ⇒ Object



1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
# File 'app/models/repository.rb', line 1308

def change_head(branch)
  if branch_exists?(branch)
    before_change_head
    raw_repository.write_ref('HEAD', "refs/heads/#{branch}")
    after_change_head
  else
    container.after_change_head_branch_does_not_exist(branch)

    false
  end
end

#changelogObject



710
711
712
# File 'app/models/repository.rb', line 710

def changelog
  file_on_head(:changelog)
end

#changelog_config(ref, path) ⇒ Object



1208
1209
1210
# File 'app/models/repository.rb', line 1208

def changelog_config(ref, path)
  blob_data_at(ref, path)
end

#cherry_pick(user, commit, branch_name, message, start_branch_name: nil, start_project: project, author_name: nil, author_email: nil, dry_run: false) ⇒ Object



1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
# File 'app/models/repository.rb', line 1039

def cherry_pick(
  user, commit, branch_name, message,
  start_branch_name: nil, start_project: project,
  author_name: nil, author_email: nil, dry_run: false
)
  target_sha = find_branch(branch_name)&.dereferenced_target&.id if branch_name.present?

  with_cache_hooks do
    raw_repository.cherry_pick(
      user: user,
      commit: commit.raw,
      branch_name: branch_name,
      message: message,
      start_branch_name: start_branch_name,
      start_repository: start_project.repository.raw_repository,
      author_name: author_name,
      author_email: author_email,
      dry_run: dry_run,
      target_sha: target_sha,
      sign: sign_commits?
    )
  end
end

#clone_as_mirror(url, http_authorization_header: "", resolved_address: "") ⇒ Object



1126
1127
1128
# File 'app/models/repository.rb', line 1126

def clone_as_mirror(url, http_authorization_header: "", resolved_address: "")
  import_repository(url, http_authorization_header: http_authorization_header, mirror: true, resolved_address: resolved_address)
end

#commit(ref = nil) ⇒ Object



124
125
126
127
128
129
# File 'app/models/repository.rb', line 124

def commit(ref = nil)
  return unless exists?
  return ref if ref.is_a?(::Commit)

  find_commit(ref || root_ref)
end

#commit_by(oid:) ⇒ Object

Finding a commit by the passed SHA Also takes care of caching, based on the SHA



133
134
135
136
137
# File 'app/models/repository.rb', line 133

def commit_by(oid:)
  return @commit_cache[oid] if @commit_cache.key?(oid)

  @commit_cache[oid] = find_commit(oid)
end

#commit_countObject



653
654
655
# File 'app/models/repository.rb', line 653

def commit_count
  root_ref ? raw_repository.commit_count(root_ref) : 0
end

#commit_count_for_ref(ref) ⇒ Object



658
659
660
661
662
# File 'app/models/repository.rb', line 658

def commit_count_for_ref(ref)
  return 0 unless exists?

  cache.fetch(:"commit_count_#{ref}") { raw_repository.commit_count(ref) }
end

#commit_files(user, **options) ⇒ Object



944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
# File 'app/models/repository.rb', line 944

def commit_files(user, **options)
  start_project = options.delete(:start_project)
  if start_project
    options[:start_repository] = start_project.repository.raw_repository
  end

  if Feature.enabled?(:commit_files_target_sha, project)
    options[:target_sha] = self.commit(options[:branch_name])&.sha || blank_ref
  else
    skip_target_sha = options.delete(:skip_target_sha)
    unless skip_target_sha
      options[:target_sha] = self.commit(options[:branch_name])&.sha
    end
  end

  with_cache_hooks { raw.commit_files(user, **options.merge(sign: sign_commits?)) }
end

#commits(ref = nil, opts = {}) ⇒ Object



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'app/models/repository.rb', line 151

def commits(ref = nil, opts = {})
  options = {
    repo: raw_repository,
    ref: ref,
    path: opts[:path],
    author: opts[:author],
    follow: Array(opts[:path]).length == 1 && Feature.disabled?(:remove_file_commit_history_following, type: :ops),
    limit: opts[:limit],
    offset: opts[:offset],
    skip_merges: !!opts[:skip_merges],
    after: opts[:after],
    before: opts[:before],
    all: !!opts[:all],
    first_parent: !!opts[:first_parent],
    order: opts[:order],
    literal_pathspec: opts.fetch(:literal_pathspec, true),
    trailers: opts[:trailers],
    include_referenced_by: opts[:include_referenced_by]
  }

  commits = Gitlab::Git::Commit.where(options)
  commits = Commit.decorate(commits, container) if commits.present?

  CommitCollection.new(container, commits, ref)
end

#commits_between(from, to, limit: nil) ⇒ Object



177
178
179
180
181
# File 'app/models/repository.rb', line 177

def commits_between(from, to, limit: nil)
  commits = Gitlab::Git::Commit.between(raw_repository, from, to, limit: limit)
  commits = Commit.decorate(commits, container) if commits.present?
  commits
end

#commits_by(oids:) ⇒ Object



139
140
141
142
143
144
145
146
147
148
149
# File 'app/models/repository.rb', line 139

def commits_by(oids:)
  return [] unless oids.present?

  commits = Gitlab::Git::Commit.batch_by_oid(raw_repository, oids)

  if commits.present?
    Commit.decorate(commits, container)
  else
    []
  end
end

#compare_source_branch(target_branch_name, source_repository, source_branch_name, straight:) ⇒ Object



1138
1139
1140
# File 'app/models/repository.rb', line 1138

def compare_source_branch(target_branch_name, source_repository, source_branch_name, straight:)
  raw_repository.compare_source_branch(target_branch_name, source_repository.raw_repository, source_branch_name, straight: straight)
end

#contribution_guideObject



705
706
707
# File 'app/models/repository.rb', line 705

def contribution_guide
  file_on_head(:contributing)
end

#contributors(ref: nil, order_by: nil, sort: 'asc') ⇒ Object

Params:

order_by: name|email|commits sort: asc|desc default: ‘asc’



828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
# File 'app/models/repository.rb', line 828

def contributors(ref: nil, order_by: nil, sort: 'asc')
  commits = self.commits(ref, limit: 2000, offset: 0, skip_merges: true)

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

    commits.each do |commit|
      if contributor.name.blank?
        contributor.name = commit.author_name
      end

      contributor.commits += 1
    end

    contributor
  end
  Commit.order_by(collection: commits, order_by: order_by, sort: sort)
end

#create_dir(user, path, **options) ⇒ Object



882
883
884
885
886
# File 'app/models/repository.rb', line 882

def create_dir(user, path, **options)
  options[:actions] = [{ action: :create_dir, file_path: path }]

  commit_files(user, **options)
end

#create_file(user, path, content, **options) ⇒ Object



894
895
896
897
# File 'app/models/repository.rb', line 894

def create_file(user, path, content, **options)
  actions = create_file_actions(path, content, execute_filemode: options.delete(:execute_filemode))
  commit_files(user, **options.merge(actions: actions))
end

#create_file_actions(path, content, execute_filemode: nil) ⇒ Object



888
889
890
891
892
# File 'app/models/repository.rb', line 888

def create_file_actions(path, content, execute_filemode: nil)
  actions = [{ action: :create, file_path: path, content: content }]
  actions << { action: :chmod, file_path: path, execute_filemode: execute_filemode } unless execute_filemode.nil?
  actions
end

#create_from_bundle(bundle_path) ⇒ Object



1287
1288
1289
1290
1291
# File 'app/models/repository.rb', line 1287

def create_from_bundle(bundle_path)
  raw.create_from_bundle(bundle_path).tap do |result|
    after_create if result
  end
end

#create_if_not_exists(default_branch = nil) ⇒ Object



1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
# File 'app/models/repository.rb', line 1271

def create_if_not_exists(default_branch = nil)
  return if exists?

  raw.create_repository(default_branch)
  after_create

  true
rescue Gitlab::Git::Repository::RepositoryExists
  # We do not want to call `#after_create` given that we didn't create the
  # repo, but we obviously have a mismatch between what's in our exists cache
  # and actual on-disk state as seen by Gitaly. Let's thus expire our caches.
  expire_status_cache

  nil
end

#create_ref(ref, ref_path) ⇒ Object



1142
1143
1144
# File 'app/models/repository.rb', line 1142

def create_ref(ref, ref_path)
  raw_repository.write_ref(ref_path, ref)
end

#delete_file(user, path, **options) ⇒ Object



927
928
929
930
931
# File 'app/models/repository.rb', line 927

def delete_file(user, path, **options)
  options[:actions] = [{ action: :delete, file_path: path }]

  commit_files(user, **options)
end

#delete_refsObject



988
989
990
# File 'app/models/repository.rb', line 988

def delete_refs(...)
  raw.delete_refs(...)
end

#diffs_by_changed_paths(diff_refs, offset = 0, batch_size = 30) ⇒ Object



1414
1415
1416
1417
1418
1419
1420
# File 'app/models/repository.rb', line 1414

def diffs_by_changed_paths(diff_refs, offset = 0, batch_size = 30)
  Gitlab::Git::BlobPairsDiffs
    .new(self)
    .diffs_by_changed_paths(diff_refs, offset, batch_size) do |diff_files|
      yield diff_files
    end
end

#empty?Boolean

We don’t need to cache the output of this method because both exists? and has_visible_content? are already memoized and cached. There’s no guarantee that the values are expired and loaded atomically.

Returns:

  • (Boolean)


635
636
637
638
639
# File 'app/models/repository.rb', line 635

def empty?
  return true unless exists?

  !has_visible_content?
end

#empty_tree_idObject



1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
# File 'app/models/repository.rb', line 1386

def empty_tree_id
  return Gitlab::Git::SHA1_EMPTY_TREE_ID unless exists?

  case object_format
  when FORMAT_SHA1
    Gitlab::Git::SHA1_EMPTY_TREE_ID
  when FORMAT_SHA256
    Gitlab::Git::SHA256_EMPTY_TREE_ID
  end
end

#exists?Boolean

Returns:

  • (Boolean)


625
626
627
628
629
# File 'app/models/repository.rb', line 625

def exists?
  return false unless full_path

  raw_repository.exists?
end

#expand_ref(ref) ⇒ Object



274
275
276
277
278
279
280
# File 'app/models/repository.rb', line 274

def expand_ref(ref)
  if tag_exists?(ref)
    Gitlab::Git::TAG_REF_PREFIX + ref
  elsif branch_exists?(ref)
    Gitlab::Git::BRANCH_REF_PREFIX + ref
  end
end

#expire_all_method_cachesObject



433
434
435
# File 'app/models/repository.rb', line 433

def expire_all_method_caches
  expire_method_caches(CACHED_METHODS)
end

#expire_ancestor_cache(ancestor_id, descendant_id) ⇒ Object



1120
1121
1122
1123
1124
# File 'app/models/repository.rb', line 1120

def expire_ancestor_cache(ancestor_id, descendant_id)
  cache_key = ancestor_cache_key(ancestor_id, descendant_id)
  request_store_cache.expire(cache_key)
  cache.expire(cache_key)
end

#expire_branch_cache(branch_name = nil) ⇒ Object



457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
# File 'app/models/repository.rb', line 457

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}")
      cache.expire(:"commit_count_#{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}")
    cache.expire(:"commit_count_#{branch_name}")
  end
end

#expire_branches_cacheObject



415
416
417
418
419
420
421
422
423
# File 'app/models/repository.rb', line 415

def expire_branches_cache
  expire_method_caches(%i[branch_names merged_branch_names branch_count has_visible_content? has_ambiguous_refs?])
  BatchLoader::Executor.clear_current if Feature.enabled?(:ref_existence_check_gitaly, project)
  expire_protected_branches_cache

  @local_branches = nil
  @branch_exists_memo = nil
  @branch_names_include = nil
end

#expire_caches_for_tagsObject



542
543
544
545
546
# File 'app/models/repository.rb', line 542

def expire_caches_for_tags
  expire_statistics_caches
  expire_emptiness_caches
  expire_tags_cache
end

#expire_content_cacheObject

expire cache that doesn’t depend on repository data (when expiring)



491
492
493
494
495
496
497
498
# File 'app/models/repository.rb', line 491

def expire_content_cache
  expire_tags_cache
  expire_branches_cache
  expire_root_ref_cache
  expire_emptiness_caches
  expire_exists_cache
  expire_statistics_caches
end

#expire_emptiness_cachesObject

Expires the cache(s) used to determine if a repository is empty or not.



479
480
481
482
483
484
# File 'app/models/repository.rb', line 479

def expire_emptiness_caches
  return unless empty?

  expire_method_caches(%i[has_visible_content?])
  raw_repository.expire_has_local_branches_cache
end

#expire_exists_cacheObject



486
487
488
# File 'app/models/repository.rb', line 486

def expire_exists_cache
  expire_method_caches(%i[exists?])
end

#expire_protected_branches_cacheObject



425
426
427
# File 'app/models/repository.rb', line 425

def expire_protected_branches_cache
  ProtectedBranches::CacheService.new(project).refresh if project # rubocop:disable CodeReuse/ServiceClass
end

#expire_root_ref_cacheObject



474
475
476
# File 'app/models/repository.rb', line 474

def expire_root_ref_cache
  expire_method_caches(%i[root_ref])
end

#expire_statistics_cachesObject



429
430
431
# File 'app/models/repository.rb', line 429

def expire_statistics_caches
  expire_method_caches(%i[size recent_objects_size commit_count])
end

#expire_status_cacheObject



500
501
502
503
504
# File 'app/models/repository.rb', line 500

def expire_status_cache
  expire_exists_cache
  expire_root_ref_cache
  expire_emptiness_caches
end

#expire_tags_cacheObject



408
409
410
411
412
413
# File 'app/models/repository.rb', line 408

def expire_tags_cache
  expire_method_caches(%i[tag_names tag_count has_ambiguous_refs?])
  BatchLoader::Executor.clear_current if Feature.enabled?(:ref_existence_check_gitaly, project)
  @tags = nil
  @tag_names_include = nil
end

#fetch_as_mirror(url, forced: false, refmap: :all_refs, prune: true, http_authorization_header: "", resolved_address: "") ⇒ Object



1130
1131
1132
# File 'app/models/repository.rb', line 1130

def fetch_as_mirror(url, forced: false, refmap: :all_refs, prune: true, http_authorization_header: "", resolved_address: "")
  fetch_remote(url, refmap: refmap, forced: forced, prune: prune, http_authorization_header: http_authorization_header, resolved_address: resolved_address)
end

#fetch_ref(source_repository, source_ref:, target_ref:) ⇒ Object



1212
1213
1214
# File 'app/models/repository.rb', line 1212

def fetch_ref(source_repository, source_ref:, target_ref:)
  raw_repository.fetch_ref(source_repository.raw_repository, source_ref: source_ref, target_ref: target_ref)
end

#fetch_source_branch!(source_repository, source_branch, local_ref) ⇒ Object



1134
1135
1136
# File 'app/models/repository.rb', line 1134

def fetch_source_branch!(source_repository, source_branch, local_ref)
  raw_repository.fetch_source_branch!(source_repository.raw_repository, source_branch, local_ref)
end

#ff_merge(user, source, target_branch, target_sha: nil, merge_request: nil) ⇒ Object



1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
# File 'app/models/repository.rb', line 1003

def ff_merge(user, source, target_branch, target_sha: nil, merge_request: nil)
  their_commit_id = commit(source)&.id
  raise 'Invalid merge source' if their_commit_id.nil?

  merge_request&.update_and_mark_in_progress_merge_commit_sha(their_commit_id)

  with_cache_hooks do
    raw.ff_merge(user,
      source_sha: their_commit_id,
      target_branch: target_branch,
      target_sha: target_sha
    )
  end
end

#file_on_head(type, object_type = :blob) ⇒ Object



1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
# File 'app/models/repository.rb', line 1178

def file_on_head(type, object_type = :blob)
  return unless head = tree(:head)

  objects =
    case object_type
    when :blob
      head.blobs
    when :tree
      head.trees
    else
      raise ArgumentError, "Object type #{object_type} is not supported"
    end

  objects.find do |object|
    Gitlab::FileDetector.type_of(object.path) == type
  end
end

#find_branch(name) ⇒ Object



228
229
230
# File 'app/models/repository.rb', line 228

def find_branch(name)
  raw_repository.find_branch(name)
end

#find_commits_by_message(query, ref = nil, path = nil, limit = 1000, offset = 0) ⇒ Object



191
192
193
194
195
196
197
# File 'app/models/repository.rb', line 191

def find_commits_by_message(query, ref = nil, path = nil, limit = 1000, offset = 0)
  return [] unless exists? && has_visible_content? && query.present?

  raw_commits = raw_repository.find_commits_by_message(query.strip, ref, path, limit, offset)
  commits = raw_commits.map { |c| commit(c) }
  CommitCollection.new(container, commits, ref)
end

#find_tag(name) ⇒ Object



232
233
234
235
236
237
238
# File 'app/models/repository.rb', line 232

def find_tag(name)
  if @tags.blank?
    raw_repository.find_tag(name)
  else
    tags.find { |tag| tag.name == name }
  end
end

#flipper_idObject



104
105
106
# File 'app/models/repository.rb', line 104

def flipper_id
  raw_repository.flipper_id
end

#get_file_attributes(revision, paths, attributes) ⇒ Object



1365
1366
1367
1368
1369
# File 'app/models/repository.rb', line 1365

def get_file_attributes(revision, paths, attributes)
  raw_repository
    .get_file_attributes(revision, paths, attributes)
    .map(&:to_h)
end

#get_patch_id(old_revision, new_revision) ⇒ Object



1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
# File 'app/models/repository.rb', line 1338

def get_patch_id(old_revision, new_revision)
  raw_repository.get_patch_id(old_revision, new_revision)
rescue Gitlab::Git::CommandError, Gitlab::Git::Repository::NoRepository, Gitlab::Git::CommandTimedOut => e
  # This is expected when there are no differences between the old_revision and the new_revision.
  # It's not ideal, but is simpler to handle this here than making breaking changes to gitaly.
  return if e.message.match?(/no difference between old and new revision./)

  Gitlab::ErrorTracking.track_exception(
    e,
    project_id: project.id,
    old_revision: old_revision,
    new_revision: new_revision
  )

  nil
end

#gitignoreObject



735
736
737
# File 'app/models/repository.rb', line 735

def gitignore
  file_on_head(:gitignore)
end

#has_ambiguous_refs?Boolean

It’s possible for a tag name to be a prefix (including slash) of a branch name, or vice versa. For instance, a tag named foo means we can’t create a tag foo/bar, but we can create a branch foo/bar.

If we know a repository has no refs of this type (which is the common case) then separating refs from paths - as in ExtractsRef - can be faster.

This method only checks one level deep, so only prefixes that contain no slashes are considered. If a repository has a tag foo/bar and a branch foo/bar/baz, it will return false.

Returns:

  • (Boolean)


254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'app/models/repository.rb', line 254

def has_ambiguous_refs?
  return false unless branch_names.present? && tag_names.present?

  with_slash = []
  no_slash = []
  (branch_names + tag_names).each do |ref|
    slash_index = ref.index('/')
    if slash_index.present?
      with_slash << ref.first(slash_index)
    else
      no_slash << ref
    end
  end

  return false if with_slash.empty?

  with_slash.intersect?(no_slash)
end

#has_gitattributes?Boolean

Returns:

  • (Boolean)


1204
1205
1206
# File 'app/models/repository.rb', line 1204

def has_gitattributes?
  blob_data_at('HEAD', '.gitattributes').present?
end

#hashObject



92
93
94
# File 'app/models/repository.rb', line 92

def hash
  [self.class, @disk_path].hash
end

#head_commitObject



749
750
751
# File 'app/models/repository.rb', line 749

def head_commit
  @head_commit ||= commit(self.root_ref)
end

#head_tree(skip_flat_paths: true) ⇒ Object



753
754
755
756
757
# File 'app/models/repository.rb', line 753

def head_tree(skip_flat_paths: true)
  return if empty? || root_ref.nil?

  @head_tree ||= Tree.new(self, root_ref, nil, skip_flat_paths: skip_flat_paths, ref_type: 'heads')
end

#health(generate) ⇒ Object



848
849
850
851
852
853
854
855
856
857
858
# File 'app/models/repository.rb', line 848

def health(generate)
  cache.fetch(:health) do
    if generate
      info = raw_repository.repository_info

      info_h = info.to_h
      info_h[:updated_at] = Time.current
      info_h
    end
  end
end

#ignore_revs_file_blobObject



1408
1409
1410
1411
1412
# File 'app/models/repository.rb', line 1408

def ignore_revs_file_blob
  return unless project&.default_branch

  blob_at(project.default_branch, Gitlab::Blame::IGNORE_REVS_FILE_NAME, limit: 0)
end

#inspectObject



120
121
122
# File 'app/models/repository.rb', line 120

def inspect
  "#<#{self.class.name}:#{@disk_path}>"
end

#issue_template_names_hashObject

store issue_template_names as hash



686
687
688
# File 'app/models/repository.rb', line 686

def issue_template_names_hash
  Gitlab::Template::IssueTemplate.repository_template_names(project)
end

#jenkinsfile?Boolean

Returns:

  • (Boolean)


740
741
742
# File 'app/models/repository.rb', line 740

def jenkinsfile?
  file_on_head(:jenkinsfile).present?
end

#keep_around(*shas, source:) ⇒ Object



389
390
391
# File 'app/models/repository.rb', line 389

def keep_around(*shas, source:)
  Gitlab::Git::KeepAround.execute(self, shas, source: source)
end

#languagesObject



383
384
385
386
387
# File 'app/models/repository.rb', line 383

def languages
  return [] if empty?

  raw_repository.languages(root_ref)
end

#last_commit_for_path(sha, path, literal_pathspec: false) ⇒ Object



788
789
790
791
# File 'app/models/repository.rb', line 788

def last_commit_for_path(sha, path, literal_pathspec: false)
  commit = raw_repository.last_commit_for_path(sha, path, literal_pathspec: literal_pathspec)
  ::Commit.new(commit, container) if commit
end

#last_commit_id_for_path(sha, path, literal_pathspec: false) ⇒ Object



793
794
795
796
797
798
799
# File 'app/models/repository.rb', line 793

def last_commit_id_for_path(sha, path, literal_pathspec: false)
  key = path.blank? ? "last_commit_id_for_path:#{sha}" : "last_commit_id_for_path:#{sha}:#{Digest::SHA1.hexdigest(path)}"

  cache.fetch(key) do
    last_commit_for_path(sha, path, literal_pathspec: literal_pathspec)&.id
  end
end

#lazy_ref_exists?(ref_name) ⇒ Boolean

Returns:

  • (Boolean)


322
323
324
325
326
327
328
329
330
331
332
# File 'app/models/repository.rb', line 322

def lazy_ref_exists?(ref_name)
  BatchLoader.for(ref_name).batch(key: self) do |ref_names, loader, _args|
    # Make a single Gitaly call to check all refs at once
    existing_refs = list_refs(ref_names).to_h { |r| [Gitlab::Git.ref_name(r.name, types: nil), true] }

    # Load results for each requested ref
    ref_names.each do |ref|
      loader.call(ref, existing_refs.key?(ref))
    end
  end
end

#lfsconfig_for(sha) ⇒ Object



1200
1201
1202
# File 'app/models/repository.rb', line 1200

def lfsconfig_for(sha)
  blob_data_at(sha, '.lfsconfig')
end

#licenseObject



724
725
726
# File 'app/models/repository.rb', line 724

def license
  license_gitaly
end

#license_blobObject



715
716
717
# File 'app/models/repository.rb', line 715

def license_blob
  file_on_head(:license)
end

#license_gitalyObject



728
729
730
731
732
# File 'app/models/repository.rb', line 728

def license_gitaly
  return unless exists?

  raw_repository.license
end

#license_keyObject



720
721
722
# File 'app/models/repository.rb', line 720

def license_key
  license&.key
end

#list_commits(ref:, query: nil, author: nil, committed_before: nil, committed_after: nil, pagination_params: { page_token: nil, limit: 1000 }) ⇒ Object



199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'app/models/repository.rb', line 199

def list_commits(
  ref:,
  query: nil,
  author: nil,
  committed_before: nil,
  committed_after: nil,
  pagination_params: { page_token: nil, limit: 1000 }
)
  return empty_commit_collection_with_next_cursor unless exists? && has_visible_content? && ref.present?

  pagination_params[:limit] ||= 1000

  response = raw_repository.list_commits(
    ref: ref,
    query: query,
    author: author,
    committed_before: committed_before,
    committed_after: committed_after,
    pagination_params: pagination_params
  )

  Repositories::CommitCollectionWithNextCursor.new(
    container,
    response.map { |c| commit(c) },
    ref,
    next_cursor: response.next_cursor
  )
end

#list_last_commits_for_tree(sha, path, offset: 0, limit: 25, literal_pathspec: false) ⇒ Object



782
783
784
785
786
# File 'app/models/repository.rb', line 782

def list_last_commits_for_tree(sha, path, offset: 0, limit: 25, literal_pathspec: false)
  raw_repository
    .list_last_commits_for_tree(sha, path, offset: offset, limit: limit, literal_pathspec: literal_pathspec)
    .transform_values { |commit| ::Commit.new(commit, container) }
end

#local_branchesObject Also known as: branches



872
873
874
# File 'app/models/repository.rb', line 872

def local_branches
  @local_branches ||= raw_repository.local_branches
end

#lookup(sha) ⇒ Object



596
597
598
599
600
# File 'app/models/repository.rb', line 596

def lookup(sha)
  strong_memoize("lookup_#{sha}") do
    raw_repository.lookup(sha)
  end
end

#ls_files(ref) ⇒ Object



1146
1147
1148
1149
# File 'app/models/repository.rb', line 1146

def ls_files(ref)
  actual_ref = ref || root_ref
  raw_repository.ls_files(actual_ref)
end

#merge(user, source_sha, merge_request, message) ⇒ Object



962
963
964
965
966
967
968
969
970
971
972
# File 'app/models/repository.rb', line 962

def merge(user, source_sha, merge_request, message)
  merge_to_branch(
    user,
    source_sha: source_sha,
    target_branch: merge_request.target_branch,
    message: message
  ) do |commit_id|
    merge_request.update_and_mark_in_progress_merge_commit_sha(commit_id)
    nil # Return value does not matter.
  end
end

#merge_base(*commits_or_ids) ⇒ Object



1101
1102
1103
1104
1105
1106
1107
# File 'app/models/repository.rb', line 1101

def merge_base(*commits_or_ids)
  commit_ids = commits_or_ids.map do |commit_or_id|
    commit_or_id.is_a?(::Commit) ? commit_or_id.id : commit_or_id
  end

  raw_repository.merge_base(*commit_ids)
end

#merge_request_template_names_hashObject



691
692
693
# File 'app/models/repository.rb', line 691

def merge_request_template_names_hash
  Gitlab::Template::MergeRequestTemplate.repository_template_names(project)
end

#merge_to_branch(user, source_sha:, target_branch:, message:, target_sha: nil) ⇒ Object



974
975
976
977
978
979
980
981
982
983
984
985
986
# File 'app/models/repository.rb', line 974

def merge_to_branch(user, source_sha:, target_branch:, message:, target_sha: nil)
  with_cache_hooks do
    raw_repository.merge(user,
      source_sha: source_sha,
      target_branch: target_branch,
      message: message,
      target_sha: target_sha,
      sign: sign_commits?
    ) do |commit_id|
      yield commit_id if block_given?
    end
  end
end

#merge_to_ref(user, source_sha:, branch:, target_ref:, message:, first_parent_ref:, expected_old_oid: '') ⇒ Object



992
993
994
995
996
997
998
999
1000
1001
# File 'app/models/repository.rb', line 992

def merge_to_ref(user, source_sha:, branch:, target_ref:, message:, first_parent_ref:, expected_old_oid: '')
  raw.merge_to_ref(user,
    source_sha: source_sha,
    branch: branch,
    target_ref: target_ref,
    message: message,
    first_parent_ref: first_parent_ref,
    expected_old_oid: expected_old_oid,
    sign: sign_commits?)
end

#merged_branch_names(branch_names = [], include_identical: false) ⇒ Object

If this method is not provided a set of branch names to check merge status, it fetches all branches.



1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
# File 'app/models/repository.rb', line 1081

def merged_branch_names(branch_names = [], include_identical: false)
  # Currently we should skip caching if requesting all branch names
  # This is only used in a few places, notably app/services/branches/delete_merged_service.rb,
  # and it could potentially result in a very large cache.
  return raw_repository.merged_branch_names(branch_names, include_identical: include_identical) if branch_names.empty? || include_identical

  cache = redis_hash_cache

  merged_branch_names_hash = cache.fetch_and_add_missing(:merged_branch_names, branch_names) do |missing_branch_names, hash|
    merged = raw_repository.merged_branch_names(missing_branch_names, include_identical: include_identical)

    missing_branch_names.each do |bn|
      # Redis only stores strings in hset keys, use a fancy encoder
      hash[bn] = Gitlab::Redis::Boolean.new(merged.include?(bn))
    end
  end

  Set.new(merged_branch_names_hash.select { |_, v| Gitlab::Redis::Boolean.true?(v) }.keys)
end

#merged_to_root_ref?(branch_or_name) ⇒ Boolean

Returns:

  • (Boolean)


1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
# File 'app/models/repository.rb', line 1063

def merged_to_root_ref?(branch_or_name)
  return unless head_commit

  branch = Gitlab::Git::Branch.find(self, branch_or_name)

  if branch
    same_head = branch.target == root_ref_sha
    merged = ancestor?(branch.target, root_ref_sha)
    !same_head && merged
  end
end

#move_dir_files_actions(path, previous_path, branch_name: nil) ⇒ Object



911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
# File 'app/models/repository.rb', line 911

def move_dir_files_actions(path, previous_path, branch_name: nil)
  regex = Regexp.new("^#{Regexp.escape(previous_path + '/')}", 'i')
  files = ls_files(branch_name)

  files.each_with_object([]) do |item, list|
    next unless regex.match?(item)

    list.push(
      action: :move,
      file_path: "#{path}/#{item[regex.match(item)[0].size..]}",
      previous_path: item,
      infer_content: true
    )
  end
end

#new_commits(newrev) ⇒ Object

Returns a list of commits that are not present in any reference



184
185
186
187
188
# File 'app/models/repository.rb', line 184

def new_commits(newrev)
  commits = raw.new_commits(newrev)

  ::Commit.decorate(commits, container)
end

#next_branch(name, opts = {}) ⇒ Object



801
802
803
804
805
806
807
808
809
810
811
812
813
814
# File 'app/models/repository.rb', line 801

def next_branch(name, opts = {})
  branch_ids = self.branch_names.map do |n|
    next 1 if n == name

    result = n.match(/\A#{name}-([0-9]+)\z/)
    result[1].to_i if result
  end.compact

  highest_branch_id = branch_ids.max || 0

  return name if opts[:mild] && 0 == highest_branch_id

  "#{name}-#{highest_branch_id + 1}"
end

#object_formatObject



1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
# File 'app/models/repository.rb', line 1371

def object_format
  cache_key = "object_format:#{full_path}"

  request_store_cache.fetch(cache_key) do
    case raw.object_format
    when :OBJECT_FORMAT_SHA1
      FORMAT_SHA1
    when :OBJECT_FORMAT_SHA256
      FORMAT_SHA256
    end
  end
rescue Gitlab::Git::Repository::NoRepository
  nil
end

#object_poolObject



1355
1356
1357
1358
1359
1360
1361
1362
1363
# File 'app/models/repository.rb', line 1355

def object_pool
  gitaly_object_pool = raw.object_pool

  return unless gitaly_object_pool

  source_project = project&.pool_repository&.source_project

  Gitlab::Git::ObjectPool.init_from_gitaly(gitaly_object_pool, source_project&.repository)
end

#path_to_repoObject

Don’t use this! It’s going away. Use Gitaly to read or write from repos.



109
110
111
112
113
114
115
116
117
118
# File 'app/models/repository.rb', line 109

def path_to_repo
  @path_to_repo ||=
    begin
      storage = Gitlab.config.repositories.storages[shard]

      File.expand_path(
        File.join(storage.legacy_disk_path, disk_path + '.git')
      )
    end
end

#projectObject



1293
1294
1295
1296
1297
1298
1299
# File 'app/models/repository.rb', line 1293

def project
  if container.is_a?(Project)
    container
  else
    container.try(:project)
  end
end

#raw_repositoryObject Also known as: raw



96
97
98
99
100
# File 'app/models/repository.rb', line 96

def raw_repository
  return unless full_path

  @raw_repository ||= initialize_raw_repository
end

#readmeObject



696
697
698
# File 'app/models/repository.rb', line 696

def readme
  head_tree&.readme
end

#readme_pathObject



700
701
702
# File 'app/models/repository.rb', line 700

def readme_path
  head_tree&.readme_path
end

#rebase(user, merge_request, skip_ci: false) ⇒ Object



1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
# File 'app/models/repository.rb', line 1216

def rebase(user, merge_request, skip_ci: false)
  push_options = []
  push_options << Gitlab::PushOptions::CI_SKIP if skip_ci

  raw.rebase(
    user,
    merge_request.id,
    branch: merge_request.source_branch,
    branch_sha: merge_request.source_branch_sha,
    remote_repository: merge_request.target_project.repository.raw,
    remote_branch: merge_request.target_branch,
    push_options: push_options
  ) do |commit_id|
    merge_request.update!(rebase_commit_sha: commit_id, merge_error: nil)
  end
rescue StandardError => e
  merge_request.update!(rebase_commit_sha: nil)
  raise e
end

#recent_objects_sizeObject

The recent objects size of this repository in mebibytes.



648
649
650
# File 'app/models/repository.rb', line 648

def recent_objects_size
  exists? ? raw_repository.recent_objects_size : 0.0
end

#ref_exists?(ref) ⇒ Boolean

Returns:

  • (Boolean)


365
366
367
368
369
# File 'app/models/repository.rb', line 365

def ref_exists?(ref)
  !!raw_repository&.ref_exists?(ref)
rescue ArgumentError
  false
end

#ref_namesObject



318
319
320
# File 'app/models/repository.rb', line 318

def ref_names
  branch_names + tag_names
end

#refresh_method_caches(types) ⇒ Object

Refreshes the method caches of this repository.

types - An Array of file types (e.g. :readme) used to refresh extra

caches.


441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
# File 'app/models/repository.rb', line 441

def refresh_method_caches(types)
  return if types.empty?

  to_refresh = []

  types.each do |type|
    methods = METHOD_CACHES_FOR_FILE_TYPES[type.to_sym]

    to_refresh.concat(Array(methods)) if methods
  end

  expire_method_caches(to_refresh)

  to_refresh.each { |method| send(method) } # rubocop:disable GitlabSecurity/PublicSend
end

#remove_prohibited_refsObject



1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
# File 'app/models/repository.rb', line 1324

def remove_prohibited_refs
  return unless exists?

  patterns = [Gitlab::Git::BRANCH_REF_PREFIX, Gitlab::Git::TAG_REF_PREFIX]

  prohibited_refs = raw_repository.list_refs(patterns).select do |ref|
    ref.name.match(Gitlab::Git::SHA_LIKE_REF)
  end

  return if prohibited_refs.blank?

  raw_repository.delete_refs(*prohibited_refs.map(&:name))
end

#revert(user, commit, branch_name, message, start_branch_name: nil, start_project: project, dry_run: false) ⇒ Object



1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
# File 'app/models/repository.rb', line 1018

def revert(
  user, commit, branch_name, message,
  start_branch_name: nil, start_project: project, dry_run: false
)
  target_sha = find_branch(branch_name)&.dereferenced_target&.id if branch_name.present?

  with_cache_hooks do
    raw_repository.revert(
      user: user,
      commit: commit.raw,
      branch_name: branch_name,
      message: message,
      start_branch_name: start_branch_name,
      start_repository: start_project.repository.raw_repository,
      dry_run: dry_run,
      target_sha: target_sha,
      sign: sign_commits?
    )
  end
end

#rm_branch(user, branch_name, target_sha: nil) ⇒ Object



300
301
302
303
304
305
306
307
# File 'app/models/repository.rb', line 300

def rm_branch(user, branch_name, target_sha: nil)
  before_remove_branch

  raw_repository.rm_branch(branch_name, user: user, target_sha: target_sha)

  after_remove_branch
  true
end

#rm_tag(user, tag_name) ⇒ Object



309
310
311
312
313
314
315
316
# File 'app/models/repository.rb', line 309

def rm_tag(user, tag_name)
  before_remove_tag

  raw_repository.rm_tag(tag_name, user: user)

  after_remove_tag
  true
end

#root_refObject



619
620
621
# File 'app/models/repository.rb', line 619

def root_ref
  raw_repository&.root_ref
end

#root_ref_shaObject



1075
1076
1077
# File 'app/models/repository.rb', line 1075

def root_ref_sha
  @root_ref_sha ||= head_commit.sha
end

#route_map_for(sha) ⇒ Object



1196
1197
1198
# File 'app/models/repository.rb', line 1196

def route_map_for(sha)
  blob_data_at(sha, '.gitlab/route-map.yml')
end

#search_branch_names(pattern) ⇒ Object



379
380
381
# File 'app/models/repository.rb', line 379

def search_branch_names(pattern)
  redis_set_cache.search('branch_names', pattern) { branch_names }
end

#search_files_by_content(query, ref, options = {}) ⇒ Object



1151
1152
1153
1154
1155
# File 'app/models/repository.rb', line 1151

def search_files_by_content(query, ref, options = {})
  return [] if empty? || query.blank?

  raw_repository.search_files_by_content(query, ref, options)
end

#search_files_by_name(query, ref) ⇒ Object



1157
1158
1159
1160
1161
# File 'app/models/repository.rb', line 1157

def search_files_by_name(query, ref)
  return [] if empty?

  raw_repository.search_files_by_name(query, ref)
end

#search_files_by_wildcard_path(path, ref = 'HEAD') ⇒ Object



1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
# File 'app/models/repository.rb', line 1163

def search_files_by_wildcard_path(path, ref = 'HEAD')
  # We need to use RE2 to match Gitaly's regexp engine
  regexp_string = RE2::Regexp.escape(path)

  any_directories_or_root = '(.*?\/)?'
  any_characters = '.*?'
  any_characters_except_slash = '([^\/])*?'

  regexp_string.gsub!('\*\*\/', any_directories_or_root)
  regexp_string.gsub!('\*\*', any_characters)
  regexp_string.gsub!('\*', any_characters_except_slash)

  raw_repository.search_files_by_regexp("^#{regexp_string}$", ref)
end

#sizeObject

The size of this repository in megabytes.



642
643
644
# File 'app/models/repository.rb', line 642

def size
  exists? ? raw_repository.size : 0.0
end

#squash(user, merge_request, message) ⇒ Object



1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
# File 'app/models/repository.rb', line 1236

def squash(user, merge_request, message)
  raw.squash(
    user,
    start_sha: merge_request.diff_start_sha,
    end_sha: merge_request.diff_head_sha,
    author: merge_request.author,
    message: message,
    sign: sign_commits?
  )
end


1247
1248
1249
# File 'app/models/repository.rb', line 1247

def submodule_links
  @submodule_links ||= ::Gitlab::SubmoduleLinks.new(self)
end

#tag_exists?(tag_name) ⇒ Boolean

Returns:

  • (Boolean)


352
353
354
355
356
357
358
359
360
361
362
363
# File 'app/models/repository.rb', line 352

def tag_exists?(tag_name)
  return false unless raw_repository

  if Feature.enabled?(:ref_existence_check_gitaly, project)
    return false unless exists?
    return false if tag_name.blank?

    return lazy_ref_exists?(Gitlab::Git::TAG_REF_PREFIX + tag_name).itself
  end

  tag_names_include?(tag_name)
end

#tag_names_contains(sha, limit: 0, exclude_refs: []) ⇒ Object



866
867
868
869
870
# File 'app/models/repository.rb', line 866

def tag_names_contains(sha, limit: 0, exclude_refs: [])
  refs = raw_repository.tag_names_contains_sha(sha, limit: adjust_containing_limit(limit: limit, exclude_refs: exclude_refs))

  adjust_containing_refs(limit: limit, refs: refs - exclude_refs)
end

#tagsObject



878
879
880
# File 'app/models/repository.rb', line 878

def tags
  @tags ||= raw_repository.tags
end

#tags_sorted_by(value, pagination_params = nil) ⇒ Object



820
821
822
# File 'app/models/repository.rb', line 820

def tags_sorted_by(value, pagination_params = nil)
  raw_repository.tags(sort_by: value, pagination_params: pagination_params)
end

#tree(sha = :head, path = nil, recursive: false, skip_flat_paths: true, pagination_params: nil, ref_type: nil, rescue_not_found: true) ⇒ Object



759
760
761
762
763
764
765
766
767
768
769
770
771
772
# File 'app/models/repository.rb', line 759

def tree(sha = :head, path = nil, recursive: false, skip_flat_paths: true, pagination_params: nil, ref_type: nil, rescue_not_found: true)
  if sha == :head
    return if empty? || root_ref.nil?

    if path.nil?
      return head_tree(skip_flat_paths: skip_flat_paths)
    else
      sha = head_commit.sha
      ref_type = nil
    end
  end

  Tree.new(self, sha, path, recursive: recursive, skip_flat_paths: skip_flat_paths, pagination_params: pagination_params, ref_type: ref_type, rescue_not_found: rescue_not_found)
end

#update_file(user, path, content, **options) ⇒ Object



906
907
908
909
# File 'app/models/repository.rb', line 906

def update_file(user, path, content, **options)
  actions = update_file_actions(path, content, previous_path: options.delete(:previous_path), execute_filemode: options.delete(:execute_filemode))
  commit_files(user, **options.merge(actions: actions))
end

#update_file_actions(path, content, previous_path: nil, execute_filemode: nil) ⇒ Object



899
900
901
902
903
904
# File 'app/models/repository.rb', line 899

def update_file_actions(path, content, previous_path: nil, execute_filemode: nil)
  action = previous_path && previous_path != path ? :move : :update
  actions = [{ action: action, file_path: path, content: content, previous_path: previous_path }]
  actions << { action: :chmod, file_path: path, execute_filemode: execute_filemode } unless execute_filemode.nil?
  actions
end

#update_submodule(user, submodule, commit_sha, message:, branch:) ⇒ Object



1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
# File 'app/models/repository.rb', line 1251

def update_submodule(user, submodule, commit_sha, message:, branch:)
  with_cache_hooks do
    raw.update_submodule(
      user: user,
      submodule: submodule,
      commit_sha: commit_sha,
      branch: branch,
      message: message
    )
  end
end

#with_cache_hooksObject



933
934
935
936
937
938
939
940
941
942
# File 'app/models/repository.rb', line 933

def with_cache_hooks
  result = yield

  return unless result

  after_create if result.repo_created?
  after_create_branch if result.branch_created?

  result.newrev
end

#xcode_project?Boolean

Returns:

  • (Boolean)


744
745
746
# File 'app/models/repository.rb', line 744

def xcode_project?
  file_on_head(:xcode_config, :tree).present?
end