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'
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.



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

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.



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

def container
  @container
end

#disk_pathObject

Returns the value of attribute disk_path.



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

def disk_path
  @disk_path
end

#full_pathObject

Returns the value of attribute full_path.



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

def full_path
  @full_path
end

#repo_typeObject

Returns the value of attribute repo_type.



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

def repo_type
  @repo_type
end

#shardObject

Returns the value of attribute shard.



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

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.



1281
1282
1283
1284
# File 'app/models/repository.rb', line 1281

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?



84
85
86
# File 'app/models/repository.rb', line 84

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



275
276
277
278
279
280
281
282
283
284
285
# File 'app/models/repository.rb', line 275

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



287
288
289
290
291
# File 'app/models/repository.rb', line 287

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.



552
553
554
555
# File 'app/models/repository.rb', line 552

def after_change_head
  expire_all_method_caches
  container.after_repository_change_head
end

#after_createObject

Runs code after a repository has been created.



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

def after_create
  expire_status_cache

  repository_event(:create_repository)
end

#after_create_branch(expire_cache: true) ⇒ Object

Runs code after a new branch has been created.



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

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.



558
559
560
561
562
563
# File 'app/models/repository.rb', line 558

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.



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

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.



547
548
549
# File 'app/models/repository.rb', line 547

def after_remove_tag
  expire_caches_for_tags
end

#ambiguous_ref?(ref) ⇒ Boolean

Returns:

  • (Boolean)


233
234
235
# File 'app/models/repository.rb', line 233

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

#ancestor?(ancestor_id, descendant_id) ⇒ Boolean

Returns:

  • (Boolean)


1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
# File 'app/models/repository.rb', line 1088

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



386
387
388
389
390
391
392
393
394
395
# File 'app/models/repository.rb', line 386

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



666
667
668
669
670
671
672
673
# File 'app/models/repository.rb', line 666

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.



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

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.



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

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.



529
530
531
# File 'app/models/repository.rb', line 529

def before_push_tag
  repository_event(:push_tag)
end

#before_remove_branchObject

Runs code before removing an existing branch.



573
574
575
576
577
# File 'app/models/repository.rb', line 573

def before_remove_branch
  expire_branches_cache

  repository_event(:remove_branch)
end

#before_remove_tagObject

Runs code before removing a tag.



540
541
542
543
544
# File 'app/models/repository.rb', line 540

def before_remove_tag
  expire_caches_for_tags

  repository_event(:remove_tag)
end

#blank_refObject



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

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



593
594
595
596
597
# File 'app/models/repository.rb', line 593

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



765
766
767
768
769
770
771
# File 'app/models/repository.rb', line 765

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



1241
1242
1243
1244
1245
1246
1247
# File 'app/models/repository.rb', line 1241

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]]



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

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)


327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
# File 'app/models/repository.rb', line 327

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



853
854
855
856
857
# File 'app/models/repository.rb', line 853

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)


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

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



809
810
811
# File 'app/models/repository.rb', line 809

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

#cacheObject



1298
1299
1300
# File 'app/models/repository.rb', line 1298

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

#cached_methodsObject



397
398
399
# File 'app/models/repository.rb', line 397

def cached_methods
  CACHED_METHODS
end

#change_head(branch) ⇒ Object



1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
# File 'app/models/repository.rb', line 1286

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



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

def changelog
  file_on_head(:changelog)
end

#changelog_config(ref, path) ⇒ Object



1187
1188
1189
# File 'app/models/repository.rb', line 1187

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



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

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
    )
  end
end

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



1105
1106
1107
# File 'app/models/repository.rb', line 1105

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



122
123
124
125
126
127
# File 'app/models/repository.rb', line 122

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



131
132
133
134
135
# File 'app/models/repository.rb', line 131

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

  @commit_cache[oid] = find_commit(oid)
end

#commit_countObject



644
645
646
# File 'app/models/repository.rb', line 644

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

#commit_count_for_ref(ref) ⇒ Object



649
650
651
652
653
# File 'app/models/repository.rb', line 649

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



937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
# File 'app/models/repository.rb', line 937

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



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

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



175
176
177
178
179
# File 'app/models/repository.rb', line 175

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



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

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



1117
1118
1119
# File 'app/models/repository.rb', line 1117

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



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

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’



821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
# File 'app/models/repository.rb', line 821

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



875
876
877
878
879
# File 'app/models/repository.rb', line 875

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



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

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



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

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



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

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



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

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



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

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

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



920
921
922
923
924
# File 'app/models/repository.rb', line 920

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

  commit_files(user, **options)
end

#delete_refsObject



980
981
982
# File 'app/models/repository.rb', line 980

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

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



1392
1393
1394
1395
1396
1397
1398
# File 'app/models/repository.rb', line 1392

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)


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

def empty?
  return true unless exists?

  !has_visible_content?
end

#empty_tree_idObject



1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
# File 'app/models/repository.rb', line 1364

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)


616
617
618
619
620
# File 'app/models/repository.rb', line 616

def exists?
  return false unless full_path

  raw_repository.exists?
end

#expand_ref(ref) ⇒ Object



267
268
269
270
271
272
273
# File 'app/models/repository.rb', line 267

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



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

def expire_all_method_caches
  expire_method_caches(CACHED_METHODS)
end

#expire_ancestor_cache(ancestor_id, descendant_id) ⇒ Object



1099
1100
1101
1102
1103
# File 'app/models/repository.rb', line 1099

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



450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
# File 'app/models/repository.rb', line 450

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



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

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



533
534
535
536
537
# File 'app/models/repository.rb', line 533

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)



484
485
486
487
488
489
490
491
# File 'app/models/repository.rb', line 484

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.



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

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



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

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

#expire_protected_branches_cacheObject



418
419
420
# File 'app/models/repository.rb', line 418

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

#expire_root_ref_cacheObject



467
468
469
# File 'app/models/repository.rb', line 467

def expire_root_ref_cache
  expire_method_caches(%i[root_ref])
end

#expire_statistics_cachesObject



422
423
424
# File 'app/models/repository.rb', line 422

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

#expire_status_cacheObject



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

def expire_status_cache
  expire_exists_cache
  expire_root_ref_cache
  expire_emptiness_caches
end

#expire_tags_cacheObject



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

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



1109
1110
1111
# File 'app/models/repository.rb', line 1109

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



1191
1192
1193
# File 'app/models/repository.rb', line 1191

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



1113
1114
1115
# File 'app/models/repository.rb', line 1113

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



984
985
986
987
988
989
990
991
992
993
994
995
996
997
# File 'app/models/repository.rb', line 984

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



1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
# File 'app/models/repository.rb', line 1157

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



221
222
223
# File 'app/models/repository.rb', line 221

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

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



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

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



225
226
227
228
229
230
231
# File 'app/models/repository.rb', line 225

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

#flipper_idObject



102
103
104
# File 'app/models/repository.rb', line 102

def flipper_id
  raw_repository.flipper_id
end

#get_file_attributes(revision, paths, attributes) ⇒ Object



1343
1344
1345
1346
1347
# File 'app/models/repository.rb', line 1343

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



1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
# File 'app/models/repository.rb', line 1316

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



726
727
728
# File 'app/models/repository.rb', line 726

def gitignore
  file_on_head(:gitignore)
end

#granular_ref_name_update(ref) ⇒ Object



1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
# File 'app/models/repository.rb', line 1400

def granular_ref_name_update(ref)
  if Gitlab::Git.tag_ref?(ref)
    cache_key = 'tag_names'
  elsif Gitlab::Git.branch_ref?(ref)
    cache_key = 'branch_names'
  else
    return
  end

  cache_value = Gitlab::Git.ref_name(ref)

  exists = ref_exists?(ref)
  operation = exists ? :add : :remove

  redis_set_cache.granular_update(cache_key, cache_value, operation)
  clear_memoization(memoizable_name(cache_key))
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)


247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'app/models/repository.rb', line 247

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)


1183
1184
1185
# File 'app/models/repository.rb', line 1183

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

#hashObject



90
91
92
# File 'app/models/repository.rb', line 90

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

#head_commitObject



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

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

#head_tree(skip_flat_paths: true) ⇒ Object



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

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



841
842
843
844
845
846
847
848
849
850
851
# File 'app/models/repository.rb', line 841

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



1386
1387
1388
1389
1390
# File 'app/models/repository.rb', line 1386

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



118
119
120
# File 'app/models/repository.rb', line 118

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

#issue_template_names_hashObject

store issue_template_names as hash



677
678
679
# File 'app/models/repository.rb', line 677

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

#jenkinsfile?Boolean

Returns:

  • (Boolean)


731
732
733
# File 'app/models/repository.rb', line 731

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

#keep_around(*shas, source:) ⇒ Object



382
383
384
# File 'app/models/repository.rb', line 382

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

#languagesObject



376
377
378
379
380
# File 'app/models/repository.rb', line 376

def languages
  return [] if empty?

  raw_repository.languages(root_ref)
end

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



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

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



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

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)


315
316
317
318
319
320
321
322
323
324
325
# File 'app/models/repository.rb', line 315

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



1179
1180
1181
# File 'app/models/repository.rb', line 1179

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

#licenseObject



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

def license
  license_gitaly
end

#license_blobObject



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

def license_blob
  file_on_head(:license)
end

#license_gitalyObject



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

def license_gitaly
  return unless exists?

  raw_repository.license
end

#license_keyObject



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

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



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'app/models/repository.rb', line 197

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

  pagination_params[:limit] ||= 1000

  raw_commits = raw_repository.list_commits(
    ref: ref,
    query: query,
    author: author,
    committed_before: committed_before,
    committed_after: committed_after,
    pagination_params: pagination_params
  )
  commits = raw_commits.map { |c| commit(c) }
  CommitCollection.new(container, commits, ref)
end

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



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

def list_last_commits_for_tree(sha, path, offset: 0, limit: 25, literal_pathspec: false)
  commits = raw_repository.list_last_commits_for_tree(sha, path, offset: offset, limit: limit, literal_pathspec: literal_pathspec)

  commits.each do |path, commit|
    commits[path] = ::Commit.new(commit, container)
  end
end

#local_branchesObject Also known as: branches



865
866
867
# File 'app/models/repository.rb', line 865

def local_branches
  @local_branches ||= raw_repository.local_branches
end

#lookup(sha) ⇒ Object



587
588
589
590
591
# File 'app/models/repository.rb', line 587

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

#ls_files(ref) ⇒ Object



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

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

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



955
956
957
958
959
960
961
962
963
964
965
# File 'app/models/repository.rb', line 955

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



1080
1081
1082
1083
1084
1085
1086
# File 'app/models/repository.rb', line 1080

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



682
683
684
# File 'app/models/repository.rb', line 682

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



967
968
969
970
971
972
973
974
975
976
977
978
# File 'app/models/repository.rb', line 967

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
    ) do |commit_id|
      yield commit_id if block_given?
    end
  end
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.



1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
# File 'app/models/repository.rb', line 1060

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)


1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
# File 'app/models/repository.rb', line 1042

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



904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
# File 'app/models/repository.rb', line 904

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



182
183
184
185
186
# File 'app/models/repository.rb', line 182

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

  ::Commit.decorate(commits, container)
end

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



794
795
796
797
798
799
800
801
802
803
804
805
806
807
# File 'app/models/repository.rb', line 794

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



1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
# File 'app/models/repository.rb', line 1349

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



1333
1334
1335
1336
1337
1338
1339
1340
1341
# File 'app/models/repository.rb', line 1333

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.



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

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



1271
1272
1273
1274
1275
1276
1277
# File 'app/models/repository.rb', line 1271

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

#raw_repositoryObject Also known as: raw



94
95
96
97
98
# File 'app/models/repository.rb', line 94

def raw_repository
  return unless full_path

  @raw_repository ||= initialize_raw_repository
end

#readmeObject



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

def readme
  head_tree&.readme
end

#readme_pathObject



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

def readme_path
  head_tree&.readme_path
end

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



1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
# File 'app/models/repository.rb', line 1195

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.



639
640
641
# File 'app/models/repository.rb', line 639

def recent_objects_size
  exists? ? raw_repository.recent_objects_size : 0.0
end

#ref_exists?(ref) ⇒ Boolean

Returns:

  • (Boolean)


358
359
360
361
362
# File 'app/models/repository.rb', line 358

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

#ref_namesObject



311
312
313
# File 'app/models/repository.rb', line 311

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.


434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
# File 'app/models/repository.rb', line 434

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



1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
# File 'app/models/repository.rb', line 1302

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



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

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
    )
  end
end

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



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

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



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

def rm_tag(user, tag_name)
  before_remove_tag

  raw_repository.rm_tag(tag_name, user: user)

  after_remove_tag
  true
end

#root_refObject



610
611
612
# File 'app/models/repository.rb', line 610

def root_ref
  raw_repository&.root_ref
end

#root_ref_shaObject



1054
1055
1056
# File 'app/models/repository.rb', line 1054

def root_ref_sha
  @root_ref_sha ||= head_commit.sha
end

#route_map_for(sha) ⇒ Object



1175
1176
1177
# File 'app/models/repository.rb', line 1175

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

#search_branch_names(pattern) ⇒ Object



372
373
374
# File 'app/models/repository.rb', line 372

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

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



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

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



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

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



1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
# File 'app/models/repository.rb', line 1142

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.



633
634
635
# File 'app/models/repository.rb', line 633

def size
  exists? ? raw_repository.size : 0.0
end

#squash(user, merge_request, message) ⇒ Object



1215
1216
1217
1218
1219
1220
1221
1222
1223
# File 'app/models/repository.rb', line 1215

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
  )
end


1225
1226
1227
# File 'app/models/repository.rb', line 1225

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

#tag_exists?(tag_name) ⇒ Boolean

Returns:

  • (Boolean)


345
346
347
348
349
350
351
352
353
354
355
356
# File 'app/models/repository.rb', line 345

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



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

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



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

def tags
  @tags ||= raw_repository.tags
end

#tags_sorted_by(value, pagination_params = nil) ⇒ Object



813
814
815
# File 'app/models/repository.rb', line 813

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



750
751
752
753
754
755
756
757
758
759
760
761
762
763
# File 'app/models/repository.rb', line 750

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



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

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



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

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



1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
# File 'app/models/repository.rb', line 1229

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



926
927
928
929
930
931
932
933
934
935
# File 'app/models/repository.rb', line 926

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)


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

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