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.



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

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) ⇒ Object



262
263
264
265
266
267
268
269
270
# File 'app/models/repository.rb', line 262

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

  after_create_branch(expire_cache: expire_cache)

  branch
rescue Gitlab::Git::Repository::InvalidRef
  false
end

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



272
273
274
275
276
# File 'app/models/repository.rb', line 272

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.



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

def after_change_head
  expire_all_method_caches
  container.after_repository_change_head
end

#after_createObject

Runs code after a repository has been created.



456
457
458
459
460
# File 'app/models/repository.rb', line 456

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.



522
523
524
525
526
# File 'app/models/repository.rb', line 522

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.



514
515
516
517
518
519
# File 'app/models/repository.rb', line 514

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.



536
537
538
# File 'app/models/repository.rb', line 536

def after_remove_branch(expire_cache: true)
  expire_branches_cache if expire_cache
end

#after_remove_tagObject

Runs code after removing a tag.



503
504
505
# File 'app/models/repository.rb', line 503

def after_remove_tag
  expire_caches_for_tags
end

#ambiguous_ref?(ref) ⇒ Boolean

Returns:

  • (Boolean)


224
225
226
# File 'app/models/repository.rb', line 224

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

#ancestor?(ancestor_id, descendant_id) ⇒ Boolean

Returns:

  • (Boolean)


1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
# File 'app/models/repository.rb', line 1030

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



340
341
342
343
344
345
346
347
348
349
# File 'app/models/repository.rb', line 340

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



619
620
621
622
623
624
625
626
# File 'app/models/repository.rb', line 619

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.



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

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.



463
464
465
466
467
468
469
470
# File 'app/models/repository.rb', line 463

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.



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

def before_push_tag
  repository_event(:push_tag)
end

#before_remove_branchObject

Runs code before removing an existing branch.



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

def before_remove_branch
  expire_branches_cache

  repository_event(:remove_branch)
end

#before_remove_tagObject

Runs code before removing a tag.



496
497
498
499
500
# File 'app/models/repository.rb', line 496

def before_remove_tag
  expire_caches_for_tags

  repository_event(:remove_tag)
end

#blank_refObject



1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
# File 'app/models/repository.rb', line 1320

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



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

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



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

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



1180
1181
1182
1183
1184
1185
1186
# File 'app/models/repository.rb', line 1180

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



553
554
555
556
557
558
559
560
561
# File 'app/models/repository.rb', line 553

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

#blobs_metadata(paths, ref = 'HEAD') ⇒ Object



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

def (paths, ref = 'HEAD')
  references = Array.wrap(paths).map { |path| [ref, path] }

  Gitlab::Git::Blob.(raw, references).map { |raw_blob| Blob.decorate(raw_blob) }
end

#branch_exists?(branch_name) ⇒ Boolean

Returns:

  • (Boolean)


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

def branch_exists?(branch_name)
  return false unless raw_repository

  branch_names_include?(branch_name)
end

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



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

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)


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

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



762
763
764
# File 'app/models/repository.rb', line 762

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

#cacheObject



1243
1244
1245
# File 'app/models/repository.rb', line 1243

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

#cached_methodsObject



351
352
353
# File 'app/models/repository.rb', line 351

def cached_methods
  CACHED_METHODS
end

#change_head(branch) ⇒ Object



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

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



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

def changelog
  file_on_head(:changelog)
end

#changelog_config(ref, path) ⇒ Object



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

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



961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
# File 'app/models/repository.rb', line 961

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



1047
1048
1049
# File 'app/models/repository.rb', line 1047

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



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

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

#commit_count_for_ref(ref) ⇒ Object



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

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



885
886
887
888
889
890
891
892
893
894
895
896
897
898
# File 'app/models/repository.rb', line 885

def commit_files(user, **options)
  start_project = options.delete(:start_project)

  if start_project
    options[:start_repository] = start_project.repository.raw_repository
  end

  skip_target_sha = options.delete(:skip_target_sha)
  unless skip_target_sha
    options[:target_sha] = self.commit(options[:branch_name])&.sha
  end

  with_cache_hooks { raw.commit_files(user, **options) }
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



1059
1060
1061
# File 'app/models/repository.rb', line 1059

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



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

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’



774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
# File 'app/models/repository.rb', line 774

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



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

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



828
829
830
831
# File 'app/models/repository.rb', line 828

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



822
823
824
825
826
# File 'app/models/repository.rb', line 822

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



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

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



1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
# File 'app/models/repository.rb', line 1188

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



1063
1064
1065
# File 'app/models/repository.rb', line 1063

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

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



868
869
870
871
872
# File 'app/models/repository.rb', line 868

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

  commit_files(user, **options)
end

#delete_refsObject



925
926
927
# File 'app/models/repository.rb', line 925

def delete_refs(...)
  raw.delete_refs(...)
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)


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

def empty?
  return true unless exists?

  !has_visible_content?
end

#empty_tree_idObject



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

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)


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

def exists?
  return false unless full_path

  raw_repository.exists?
end

#expand_ref(ref) ⇒ Object



254
255
256
257
258
259
260
# File 'app/models/repository.rb', line 254

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



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

def expire_all_method_caches
  expire_method_caches(CACHED_METHODS)
end

#expire_ancestor_cache(ancestor_id, descendant_id) ⇒ Object



1041
1042
1043
1044
1045
# File 'app/models/repository.rb', line 1041

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_avatar_cacheObject



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

def expire_avatar_cache
  expire_method_caches(%i[avatar])
end

#expire_branch_cache(branch_name = nil) ⇒ Object



406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
# File 'app/models/repository.rb', line 406

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



361
362
363
364
365
366
367
368
# File 'app/models/repository.rb', line 361

def expire_branches_cache
  expire_method_caches(%i[branch_names merged_branch_names branch_count has_visible_content? has_ambiguous_refs?])
  expire_protected_branches_cache

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

#expire_caches_for_tagsObject



489
490
491
492
493
# File 'app/models/repository.rb', line 489

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)



440
441
442
443
444
445
446
447
# File 'app/models/repository.rb', line 440

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.



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

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



435
436
437
# File 'app/models/repository.rb', line 435

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

#expire_protected_branches_cacheObject



370
371
372
# File 'app/models/repository.rb', line 370

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

#expire_root_ref_cacheObject



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

def expire_root_ref_cache
  expire_method_caches(%i[root_ref])
end

#expire_statistics_cachesObject



374
375
376
# File 'app/models/repository.rb', line 374

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

#expire_status_cacheObject



449
450
451
452
453
# File 'app/models/repository.rb', line 449

def expire_status_cache
  expire_exists_cache
  expire_root_ref_cache
  expire_emptiness_caches
end

#expire_tags_cacheObject



355
356
357
358
359
# File 'app/models/repository.rb', line 355

def expire_tags_cache
  expire_method_caches(%i[tag_names tag_count has_ambiguous_refs?])
  @tags = nil
  @tag_names_include = nil
end

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



1051
1052
1053
# File 'app/models/repository.rb', line 1051

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



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

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



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

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



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

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



1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
# File 'app/models/repository.rb', line 1096

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



212
213
214
# File 'app/models/repository.rb', line 212

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
196
197
198
# File 'app/models/repository.rb', line 189

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

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

#find_tag(name) ⇒ Object



216
217
218
219
220
221
222
# File 'app/models/repository.rb', line 216

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



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

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



1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
# File 'app/models/repository.rb', line 1261

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



679
680
681
# File 'app/models/repository.rb', line 679

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)


238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'app/models/repository.rb', line 238

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

  with_slash, no_slash = (branch_names + tag_names).partition { |ref| ref.include?('/') }

  return false if with_slash.empty?

  prefixes = no_slash.map { |ref| Regexp.escape(ref) }.join('|')
  prefix_regex = %r{^(#{prefixes})/}

  with_slash.any? do |ref|
    prefix_regex.match?(ref)
  end
end

#has_gitattributes?Boolean

Returns:

  • (Boolean)


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

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



693
694
695
# File 'app/models/repository.rb', line 693

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

#head_tree(skip_flat_paths: true) ⇒ Object



697
698
699
700
701
# File 'app/models/repository.rb', line 697

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

#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



630
631
632
# File 'app/models/repository.rb', line 630

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

#jenkinsfile?Boolean

Returns:

  • (Boolean)


684
685
686
# File 'app/models/repository.rb', line 684

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

#keep_around(*shas, source:) ⇒ Object



336
337
338
# File 'app/models/repository.rb', line 336

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

#languagesObject



330
331
332
333
334
# File 'app/models/repository.rb', line 330

def languages
  return [] if empty?

  raw_repository.languages(root_ref)
end

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



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

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



739
740
741
742
743
744
745
# File 'app/models/repository.rb', line 739

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

#lfsconfig_for(sha) ⇒ Object



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

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

#licenseObject



668
669
670
# File 'app/models/repository.rb', line 668

def license
  license_gitaly
end

#license_blobObject



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

def license_blob
  file_on_head(:license)
end

#license_gitalyObject



672
673
674
675
676
# File 'app/models/repository.rb', line 672

def license_gitaly
  return unless exists?

  raw_repository.license
end

#license_keyObject



664
665
666
# File 'app/models/repository.rb', line 664

def license_key
  license&.key
end

#list_commits_by(query, ref, author: nil, before: nil, after: nil, limit: 1000) ⇒ Object



200
201
202
203
204
205
206
207
208
209
210
# File 'app/models/repository.rb', line 200

def list_commits_by(query, ref, author: nil, before: nil, after: nil, limit: 1000)
  return [] unless exists?
  return [] unless has_visible_content?
  return [] unless ref.present?

  commits = raw_repository.list_commits_by(
    query, ref, author: author, before: before, after: after, limit: limit).map do |c|
    commit(c)
  end
  CommitCollection.new(container, commits, ref)
end

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



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

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



806
807
808
# File 'app/models/repository.rb', line 806

def local_branches
  @local_branches ||= raw_repository.local_branches
end

#lookup(sha) ⇒ Object



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

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

#ls_files(ref) ⇒ Object



1067
1068
1069
1070
# File 'app/models/repository.rb', line 1067

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

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



900
901
902
903
904
905
906
907
908
909
910
# File 'app/models/repository.rb', line 900

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



1022
1023
1024
1025
1026
1027
1028
# File 'app/models/repository.rb', line 1022

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



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

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



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

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 = []) ⇒ Object

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



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

def merged_branch_names(branch_names = [])
  # 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) if branch_names.empty?

  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)

    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)


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

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(user, path, previous_path, **options) ⇒ Object



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

def move_dir_files(user, path, previous_path, **options)
  actions = move_dir_files_actions(path, previous_path, branch_name: options[:branch_name])
  return if actions.blank?

  commit_files(user, **options.merge(actions: actions))
end

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



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

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



747
748
749
750
751
752
753
754
755
756
757
758
759
760
# File 'app/models/repository.rb', line 747

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



1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
# File 'app/models/repository.rb', line 1294

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



1278
1279
1280
1281
1282
1283
1284
1285
1286
# File 'app/models/repository.rb', line 1278

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



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

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



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

def readme
  head_tree&.readme
end

#readme_pathObject



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

def readme_path
  head_tree&.readme_path
end

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



1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
# File 'app/models/repository.rb', line 1134

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.



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

def recent_objects_size
  exists? ? raw_repository.recent_objects_size : 0.0
end

#ref_exists?(ref) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#ref_namesObject



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

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.


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

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



1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
# File 'app/models/repository.rb', line 1247

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



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

def revert(
  user, commit, branch_name, message,
  start_branch_name: nil, start_project: project, dry_run: false)

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

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



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

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



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

def rm_tag(user, tag_name)
  before_remove_tag

  raw_repository.rm_tag(tag_name, user: user)

  after_remove_tag
  true
end

#root_refObject



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

def root_ref
  raw_repository&.root_ref
end

#root_ref_shaObject



996
997
998
# File 'app/models/repository.rb', line 996

def root_ref_sha
  @root_ref_sha ||= head_commit.sha
end

#route_map_for(sha) ⇒ Object



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

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

#search_branch_names(pattern) ⇒ Object



326
327
328
# File 'app/models/repository.rb', line 326

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

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



1072
1073
1074
1075
1076
# File 'app/models/repository.rb', line 1072

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



1078
1079
1080
1081
1082
# File 'app/models/repository.rb', line 1078

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



1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
# File 'app/models/repository.rb', line 1084

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)

  anything = '.*?'
  anything_but_not_slash = '([^\/])*?'
  regexp_string.gsub!('\*\*', anything)
  regexp_string.gsub!('\*', anything_but_not_slash)

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

#sizeObject

The size of this repository in megabytes.



586
587
588
# File 'app/models/repository.rb', line 586

def size
  exists? ? raw_repository.size : 0.0
end

#squash(user, merge_request, message) ⇒ Object



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

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


1164
1165
1166
# File 'app/models/repository.rb', line 1164

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

#tag_exists?(tag_name) ⇒ Boolean

Returns:

  • (Boolean)


306
307
308
309
310
# File 'app/models/repository.rb', line 306

def tag_exists?(tag_name)
  return false unless raw_repository

  tag_names_include?(tag_name)
end

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



800
801
802
803
804
# File 'app/models/repository.rb', line 800

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



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

def tags
  @tags ||= raw_repository.tags
end

#tags_sorted_by(value, pagination_params = nil) ⇒ Object



766
767
768
# File 'app/models/repository.rb', line 766

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



703
704
705
706
707
708
709
710
711
712
713
714
715
716
# File 'app/models/repository.rb', line 703

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



840
841
842
843
# File 'app/models/repository.rb', line 840

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



833
834
835
836
837
838
# File 'app/models/repository.rb', line 833

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



1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
# File 'app/models/repository.rb', line 1168

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



874
875
876
877
878
879
880
881
882
883
# File 'app/models/repository.rb', line 874

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)


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

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