Class: Gitlab::Git::Repository

Inherits:
Object
  • Object
show all
Includes:
EncodingHelper, RepositoryMirroring, Gitlab::Git::RuggedImpl::Repository, WrapsGitalyErrors, Utils::StrongMemoize
Defined in:
lib/gitlab/git/repository.rb

Defined Under Namespace

Classes: CreateTreeError

Constant Summary collapse

SEARCH_CONTEXT_LINES =
3
REV_LIST_COMMIT_LIMIT =
2_000
GITALY_INTERNAL_URL =
'ssh://gitaly/internal.git'
GITLAB_PROJECTS_TIMEOUT =
Gitlab.config.gitlab_shell.git_timeout
EMPTY_REPOSITORY_CHECKSUM =
'0000000000000000000000000000000000000000'
NoRepository =
Class.new(::Gitlab::Git::BaseError)
RepositoryExists =
Class.new(::Gitlab::Git::BaseError)
InvalidRepository =
Class.new(::Gitlab::Git::BaseError)
InvalidBlobName =
Class.new(::Gitlab::Git::BaseError)
InvalidRef =
Class.new(::Gitlab::Git::BaseError)
GitError =
Class.new(::Gitlab::Git::BaseError)
DeleteBranchError =
Class.new(::Gitlab::Git::BaseError)
TagExistsError =
Class.new(::Gitlab::Git::BaseError)
ChecksumError =
Class.new(::Gitlab::Git::BaseError)

Constants included from EncodingHelper

EncodingHelper::ENCODING_CONFIDENCE_THRESHOLD, EncodingHelper::ESCAPED_CHARS, EncodingHelper::UNICODE_REPLACEMENT_CHARACTER

Constants included from Gitlab::Git::RuggedImpl::Repository

Gitlab::Git::RuggedImpl::Repository::ALLOWED_OBJECT_RELATIVE_DIRECTORIES_VARIABLES, Gitlab::Git::RuggedImpl::Repository::FEATURE_FLAGS

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Utils::StrongMemoize

#clear_memoization, #strong_memoize, #strong_memoized?

Methods included from EncodingHelper

#binary_io, #detect_binary?, #detect_encoding, #detect_libgit2_binary?, #encode!, #encode_binary, #encode_utf8, #encode_utf8_no_detect, #encode_utf8_with_replacement_character, #unquote_path

Methods included from WrapsGitalyErrors

#wrapped_gitaly_errors

Methods included from RepositoryMirroring

#remote_branches

Methods included from Gitlab::Git::RuggedImpl::Repository

#alternate_object_directories, #cleanup, #lookup, #relative_object_directories, #rev_parse_target, #rugged, #rugged_is_ancestor?, #rugged_merge_base

Methods included from Utils::Override

#extended, extensions, #included, #method_added, #override, #prepended, #queue_verification, verify!

Methods included from Gitlab::Git::RuggedImpl::UseRugged

#execute_rugged_call, #rugged_enabled_through_feature_flag?, #rugged_feature_keys, #running_puma_with_multiple_threads?, #use_rugged?

Constructor Details

#initialize(storage, relative_path, gl_repository, gl_project_path) ⇒ Repository

This initializer method is only used on the client side (gitlab-ce). Gitaly-ruby uses a different initializer.


59
60
61
62
63
64
65
66
# File 'lib/gitlab/git/repository.rb', line 59

def initialize(storage, relative_path, gl_repository, gl_project_path)
  @storage = storage
  @relative_path = relative_path
  @gl_repository = gl_repository
  @gl_project_path = gl_project_path

  @name = @relative_path.split("/").last
end

Instance Attribute Details

#gl_project_pathObject (readonly)

Returns the value of attribute gl_project_path.


48
49
50
# File 'lib/gitlab/git/repository.rb', line 48

def gl_project_path
  @gl_project_path
end

#gl_repositoryObject (readonly) Also known as: object_pool_remote_name

Returns the value of attribute gl_repository.


48
49
50
# File 'lib/gitlab/git/repository.rb', line 48

def gl_repository
  @gl_repository
end

#nameObject (readonly)

Directory name of repo


43
44
45
# File 'lib/gitlab/git/repository.rb', line 43

def name
  @name
end

#relative_pathObject (readonly)

Relative path of repo


46
47
48
# File 'lib/gitlab/git/repository.rb', line 46

def relative_path
  @relative_path
end

#storageObject (readonly)

Returns the value of attribute storage.


48
49
50
# File 'lib/gitlab/git/repository.rb', line 48

def storage
  @storage
end

Instance Method Details

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


72
73
74
# File 'lib/gitlab/git/repository.rb', line 72

def ==(other)
  other.is_a?(self.class) && [storage, relative_path] == [other.storage, other.relative_path]
end

#add_branch(branch_name, user:, target:) ⇒ Object


600
601
602
603
604
# File 'lib/gitlab/git/repository.rb', line 600

def add_branch(branch_name, user:, target:)
  wrapped_gitaly_errors do
    gitaly_operation_client.user_create_branch(branch_name, user, target)
  end
end

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


606
607
608
609
610
# File 'lib/gitlab/git/repository.rb', line 606

def add_tag(tag_name, user:, target:, message: nil)
  wrapped_gitaly_errors do
    gitaly_operation_client.add_tag(tag_name, user, target, message)
  end
end

#ancestor?(from, to) ⇒ Boolean

Returns true is from is direct ancestor to to, otherwise false

Returns:

  • (Boolean)

456
457
458
# File 'lib/gitlab/git/repository.rb', line 456

def ancestor?(from, to)
  gitaly_commit_client.ancestor?(from, to)
end

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


250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/gitlab/git/repository.rb', line 250

def (ref, storage_path, project_path, format = "tar.gz", append_sha:, path: nil)
  ref ||= root_ref
  commit = Gitlab::Git::Commit.find(self, ref)
  return {} if commit.nil?

  prefix = archive_prefix(ref, commit.id, project_path, append_sha: append_sha, path: path)

  {
    'ArchivePrefix' => prefix,
    'ArchivePath' => archive_file_path(storage_path, commit.id, prefix, format),
    'CommitId' => commit.id,
    'GitalyRepository' => gitaly_repository.to_h
  }
end

#attributes(path) ⇒ Object

Returns the Git attributes for the given file path.

See `Gitlab::Git::Attributes` for more information.


751
752
753
# File 'lib/gitlab/git/repository.rb', line 751

def attributes(path)
  info_attributes.attributes(path)
end

#attributes_at(ref) ⇒ Object

Returns parsed .gitattributes for a given ref

This only parses the root .gitattributes file, it does not traverse subfolders to find additional .gitattributes files

This method is around 30 times slower than `attributes`, which uses `$GIT_DIR/info/attributes`. Consider caching AttributesAtRefParser and reusing that for multiple calls instead of this method.


767
768
769
# File 'lib/gitlab/git/repository.rb', line 767

def attributes_at(ref)
  AttributesAtRefParser.new(self, ref)
end

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

Items should be of format [[commit_id, path], [commit_id1, path1]]


857
858
859
# File 'lib/gitlab/git/repository.rb', line 857

def batch_blobs(items, blob_size_limit: Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE)
  Gitlab::Git::Blob.batch(self, items, blob_size_limit: blob_size_limit)
end

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


852
853
854
# File 'lib/gitlab/git/repository.rb', line 852

def blob_at(sha, path, limit: Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE)
  Gitlab::Git::Blob.find(self, sha, path, limit: limit) unless Gitlab::Git.blank_ref?(sha)
end

#blobs(revisions, with_paths: false, dynamic_timeout: nil) ⇒ Object

List blobs reachable via a set of revisions. Supports the pseudo-revisions `–not` and `–all`. Uses the minimum of GitalyClient.medium_timeout and dynamic timeout if the dynamic timeout is set, otherwise it'll always use the medium timeout.


392
393
394
395
396
397
398
399
400
401
# File 'lib/gitlab/git/repository.rb', line 392

def blobs(revisions, with_paths: false, dynamic_timeout: nil)
  revisions = revisions.reject { |rev| rev.blank? || rev == ::Gitlab::Git::BLANK_SHA }

  return [] if revisions.blank?

  wrapped_gitaly_errors do
    gitaly_blob_client.list_blobs(revisions, limit: REV_LIST_COMMIT_LIMIT,
                                  with_paths: with_paths, dynamic_timeout: dynamic_timeout)
  end
end

#branch_countObject

Returns the number of valid branches


147
148
149
150
151
# File 'lib/gitlab/git/repository.rb', line 147

def branch_count
  wrapped_gitaly_errors do
    gitaly_ref_client.count_branch_names
  end
end

#branch_exists?(name) ⇒ Boolean

Returns true if the given branch exists

name - The name of the branch as a String.

Returns:

  • (Boolean)

233
234
235
236
237
# File 'lib/gitlab/git/repository.rb', line 233

def branch_exists?(name)
  wrapped_gitaly_errors do
    gitaly_ref_exists?("refs/heads/#{name}")
  end
end

#branch_namesObject

Returns an Array of branch names sorted by name ASC


112
113
114
115
116
# File 'lib/gitlab/git/repository.rb', line 112

def branch_names
  wrapped_gitaly_errors do
    gitaly_ref_client.branch_names
  end
end

#branch_names_contains_sha(sha) ⇒ Object


975
976
977
# File 'lib/gitlab/git/repository.rb', line 975

def branch_names_contains_sha(sha)
  gitaly_ref_client.branch_names_contains_sha(sha)
end

#branchesObject

Returns an Array of Branches


119
120
121
122
123
# File 'lib/gitlab/git/repository.rb', line 119

def branches
  wrapped_gitaly_errors do
    gitaly_ref_client.branches
  end
end

#bundle_to_disk(save_path) ⇒ Object


901
902
903
904
905
906
907
# File 'lib/gitlab/git/repository.rb', line 901

def bundle_to_disk(save_path)
  wrapped_gitaly_errors do
    gitaly_repository_client.create_bundle(save_path)
  end

  true
end

#can_be_merged?(source_sha, target_branch) ⇒ Boolean

Returns:

  • (Boolean)

992
993
994
995
996
997
998
# File 'lib/gitlab/git/repository.rb', line 992

def can_be_merged?(source_sha, target_branch)
  if target_sha = find_branch(target_branch)&.target
    !gitaly_conflicts_client(source_sha, target_sha).conflicts?
  else
    false
  end
end

#checksumObject


1039
1040
1041
1042
1043
1044
1045
1046
# File 'lib/gitlab/git/repository.rb', line 1039

def checksum
  # The exists? RPC is much cheaper, so we perform this request first
  raise NoRepository, "Repository does not exists" unless exists?

  gitaly_repository_client.calculate_checksum
rescue GRPC::NotFound
  raise NoRepository # Guard against data races.
end

#cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:, dry_run: false) ⇒ Object


664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
# File 'lib/gitlab/git/repository.rb', line 664

def cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:, dry_run: false)
  args = {
    user: user,
    commit: commit,
    branch_name: branch_name,
    message: message,
    start_branch_name: start_branch_name,
    start_repository: start_repository,
    dry_run: dry_run
  }

  wrapped_gitaly_errors do
    gitaly_operation_client.user_cherry_pick(**args)
  end
end

#clean(options = {}) ⇒ Object

Mimic the `git clean` command and recursively delete untracked files. Valid keys that can be passed in the options hash are:

:d - Remove untracked directories :f - Remove untracked directories that are managed by a different

repository

:x - Remove ignored files

The value in options must evaluate to true for an option to take effect.

Examples:

repo.clean(d: true, f: true) # Enable the -d and -f options

repo.clean(d: false, x: true) # -x is enabled, -d is not

592
593
594
595
596
597
598
# File 'lib/gitlab/git/repository.rb', line 592

def clean(options = {})
  strategies = [:remove_untracked]
  strategies.push(:force) if options[:f]
  strategies.push(:remove_ignored) if options[:x]

  # TODO: implement this method
end

#commit(ref = 'HEAD') ⇒ Object

Refactoring aid; allows us to copy code from app/models/repository.rb


810
811
812
# File 'lib/gitlab/git/repository.rb', line 810

def commit(ref = 'HEAD')
  Gitlab::Git::Commit.find(self, ref)
end

#commit_count(ref) ⇒ Object

Return total commits count accessible from passed ref


563
564
565
566
567
# File 'lib/gitlab/git/repository.rb', line 563

def commit_count(ref)
  wrapped_gitaly_errors do
    gitaly_commit_client.commit_count(ref)
  end
end

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


789
790
791
792
793
# File 'lib/gitlab/git/repository.rb', line 789

def compare_source_branch(target_branch_name, source_repository, source_branch_name, straight:)
  CrossRepoComparer
    .new(source_repository, self)
    .compare(source_branch_name, target_branch_name, straight: straight)
end

#copy_gitattributes(ref) ⇒ Object


735
736
737
738
739
# File 'lib/gitlab/git/repository.rb', line 735

def copy_gitattributes(ref)
  wrapped_gitaly_errors do
    gitaly_repository_client.apply_gitattributes(ref)
  end
end

#count_commits(options) ⇒ Object


403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
# File 'lib/gitlab/git/repository.rb', line 403

def count_commits(options)
  options = process_count_commits_options(options.dup)

  wrapped_gitaly_errors do
    if options[:left_right]
      from = options[:from]
      to = options[:to]

      right_count = gitaly_commit_client
        .commit_count("#{from}..#{to}", options)
      left_count = gitaly_commit_client
        .commit_count("#{to}..#{from}", options)

      [left_count, right_count]
    else
      gitaly_commit_client.commit_count(options[:ref], options)
    end
  end
end

#count_commits_between(from, to, options = {}) ⇒ Object

Counts the amount of commits between `from` and `to`.


424
425
426
# File 'lib/gitlab/git/repository.rb', line 424

def count_commits_between(from, to, options = {})
  count_commits(from: from, to: to, **options)
end

#create_branch(ref, start_point = "HEAD") ⇒ Object

Create a new branch named **ref+ based on **stat_point+, HEAD by default Note: No Git hooks are executed for this action

Examples:

create_branch("feature")
create_branch("other-feature", "master")

714
715
716
# File 'lib/gitlab/git/repository.rb', line 714

def create_branch(ref, start_point = "HEAD")
  write_ref(ref, start_point)
end

#create_from_bundle(bundle_path) ⇒ Object


867
868
869
870
871
872
873
874
# File 'lib/gitlab/git/repository.rb', line 867

def create_from_bundle(bundle_path)
  # It's important to check that the linked-to file is actually a valid
  # .bundle file as it is passed to `git clone`, which may otherwise
  # interpret it as a pointer to another repository
  ::Gitlab::Git::BundleFile.check!(bundle_path)

  gitaly_repository_client.create_from_bundle(bundle_path)
end

#create_from_snapshot(url, auth) ⇒ Object


876
877
878
# File 'lib/gitlab/git/repository.rb', line 876

def create_from_snapshot(url, auth)
  gitaly_repository_client.create_from_snapshot(url, auth)
end

#create_repository(default_branch = nil) ⇒ Object


102
103
104
105
106
107
108
# File 'lib/gitlab/git/repository.rb', line 102

def create_repository(default_branch = nil)
  wrapped_gitaly_errors do
    gitaly_repository_client.create_repository(default_branch)
  rescue GRPC::AlreadyExists => e
    raise RepositoryExists, e.message
  end
end

#delete_all_refs_except(prefixes) ⇒ Object


244
245
246
247
248
# File 'lib/gitlab/git/repository.rb', line 244

def delete_all_refs_except(prefixes)
  wrapped_gitaly_errors do
    gitaly_ref_client.delete_refs(except_with_prefixes: prefixes)
  end
end

#delete_branch(branch_name) ⇒ Object

Delete the specified branch from the repository Note: No Git hooks are executed for this action


696
697
698
699
700
# File 'lib/gitlab/git/repository.rb', line 696

def delete_branch(branch_name)
  write_ref(branch_name, Gitlab::Git::BLANK_SHA)
rescue CommandError => e
  raise DeleteBranchError, e
end

#delete_refs(*ref_names) ⇒ Object


702
703
704
705
706
# File 'lib/gitlab/git/repository.rb', line 702

def delete_refs(*ref_names)
  wrapped_gitaly_errors do
    gitaly_delete_refs(*ref_names)
  end
end

#diff(from, to, options = {}, *paths) ⇒ Object

Return an array of Diff objects that represent the diff between from and to. See Diff::filter_diff_options for the allowed diff options. The options hash can also include :break_rewrites to split larger rewrites into delete/add pairs.


478
479
480
481
482
# File 'lib/gitlab/git/repository.rb', line 478

def diff(from, to, options = {}, *paths)
  iterator = gitaly_commit_client.diff(from, to, options.merge(paths: paths))

  Gitlab::Git::DiffCollection.new(iterator, options)
end

#diff_stats(left_id, right_id) ⇒ Object


484
485
486
487
488
489
490
491
492
493
494
495
496
# File 'lib/gitlab/git/repository.rb', line 484

def diff_stats(left_id, right_id)
  if [left_id, right_id].any? { |ref| ref.blank? || Gitlab::Git.blank_ref?(ref) }
    return empty_diff_stats
  end

  stats = wrapped_gitaly_errors do
    gitaly_commit_client.diff_stats(left_id, right_id)
  end

  Gitlab::Git::DiffStatsCollection.new(stats)
rescue CommandError, TypeError
  empty_diff_stats
end

#disconnect_alternatesObject


933
934
935
936
937
# File 'lib/gitlab/git/repository.rb', line 933

def disconnect_alternates
  wrapped_gitaly_errors do
    gitaly_repository_client.disconnect_alternates
  end
end

#diverging_commit_count(from, to, max_count: 0) ⇒ Object

Return total diverging commits count


570
571
572
573
574
# File 'lib/gitlab/git/repository.rb', line 570

def diverging_commit_count(from, to, max_count: 0)
  wrapped_gitaly_errors do
    gitaly_commit_client.diverging_commit_count(from, to, max_count: max_count)
  end
end

#empty?Boolean

Returns:

  • (Boolean)

814
815
816
# File 'lib/gitlab/git/repository.rb', line 814

def empty?
  !has_visible_content?
end

#exists?Boolean

Returns:

  • (Boolean)

98
99
100
# File 'lib/gitlab/git/repository.rb', line 98

def exists?
  gitaly_repository_client.exists?
end

#expire_has_local_branches_cacheObject


173
174
175
# File 'lib/gitlab/git/repository.rb', line 173

def expire_has_local_branches_cache
  clear_memoization(:has_local_branches)
end

#fetch_remote(url, refmap: nil, ssh_auth: nil, forced: false, no_tags: false, prune: true, check_tags_changed: false, http_authorization_header: "") ⇒ Object

Fetch remote for repository

remote - remote name url - URL of the remote to fetch. `remote` is not used in this case. refmap - if url is given, determines which references should get fetched where ssh_auth - SSH known_hosts data and a private key to use for public-key authentication forced - should we use –force flag? no_tags - should we use –no-tags flag? prune - should we use –prune flag? check_tags_changed - should we ask gitaly to calculate whether any tags changed?


828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
# File 'lib/gitlab/git/repository.rb', line 828

def fetch_remote(url, refmap: nil, ssh_auth: nil, forced: false, no_tags: false, prune: true, check_tags_changed: false, http_authorization_header: "")
  wrapped_gitaly_errors do
    gitaly_repository_client.fetch_remote(
      url,
      refmap: refmap,
      ssh_auth: ssh_auth,
      forced: forced,
      no_tags: no_tags,
      prune: prune,
      check_tags_changed: check_tags_changed,
      timeout: GITLAB_PROJECTS_TIMEOUT,
      http_authorization_header: http_authorization_header
    )
  end
end

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


783
784
785
786
787
# File 'lib/gitlab/git/repository.rb', line 783

def fetch_source_branch!(source_repository, source_branch, local_ref)
  wrapped_gitaly_errors do
    gitaly_repository_client.fetch_source_branch(source_repository, source_branch, local_ref)
  end
end

#ff_merge(user, source_sha, target_branch) ⇒ Object


642
643
644
645
646
# File 'lib/gitlab/git/repository.rb', line 642

def ff_merge(user, source_sha, target_branch)
  wrapped_gitaly_errors do
    gitaly_operation_client.user_ff_branch(user, source_sha, target_branch)
  end
end

#find_branch(name) ⇒ Object

Directly find a branch with a simple name (e.g. master)


127
128
129
130
131
# File 'lib/gitlab/git/repository.rb', line 127

def find_branch(name)
  wrapped_gitaly_errors do
    gitaly_ref_client.find_branch(name)
  end
end

#find_changed_paths(commits) ⇒ Object


498
499
500
501
502
503
504
505
506
507
508
# File 'lib/gitlab/git/repository.rb', line 498

def find_changed_paths(commits)
  processed_commits = commits.reject { |ref| ref.blank? || Gitlab::Git.blank_ref?(ref) }

  return [] if processed_commits.empty?

  wrapped_gitaly_errors do
    gitaly_commit_client.find_changed_paths(processed_commits)
  end
rescue CommandError, TypeError, NoRepository
  []
end

#find_commits_by_message(query, ref, path, limit, offset) ⇒ Object


1013
1014
1015
1016
1017
1018
1019
# File 'lib/gitlab/git/repository.rb', line 1013

def find_commits_by_message(query, ref, path, limit, offset)
  wrapped_gitaly_errors do
    gitaly_commit_client
      .commits_by_message(query, revision: ref, path: path, limit: limit, offset: offset)
      .map { |c| commit(c) }
  end
end

#find_remote_root_ref(remote_url, authorization = nil) ⇒ Object


718
719
720
721
722
723
724
# File 'lib/gitlab/git/repository.rb', line 718

def find_remote_root_ref(remote_url, authorization = nil)
  return unless remote_url.present?

  wrapped_gitaly_errors do
    gitaly_remote_client.find_remote_root_ref(remote_url, authorization)
  end
end

#find_tag(name) ⇒ Object


133
134
135
136
137
138
# File 'lib/gitlab/git/repository.rb', line 133

def find_tag(name)
  wrapped_gitaly_errors do
    gitaly_ref_client.find_tag(name)
  end
rescue CommandError
end

#fsckObject

Raises:


861
862
863
864
865
# File 'lib/gitlab/git/repository.rb', line 861

def fsck
  msg, status = gitaly_repository_client.fsck

  raise GitError, "Could not fsck repository: #{msg}" unless status == 0
end

#gitaly_blob_clientObject


963
964
965
# File 'lib/gitlab/git/repository.rb', line 963

def gitaly_blob_client
  @gitaly_blob_client ||= Gitlab::GitalyClient::BlobService.new(self)
end

#gitaly_commit_clientObject


947
948
949
# File 'lib/gitlab/git/repository.rb', line 947

def gitaly_commit_client
  @gitaly_commit_client ||= Gitlab::GitalyClient::CommitService.new(self)
end

#gitaly_conflicts_client(our_commit_oid, their_commit_oid) ⇒ Object


967
968
969
# File 'lib/gitlab/git/repository.rb', line 967

def gitaly_conflicts_client(our_commit_oid, their_commit_oid)
  Gitlab::GitalyClient::ConflictsService.new(self, our_commit_oid, their_commit_oid)
end

#gitaly_operation_clientObject


955
956
957
# File 'lib/gitlab/git/repository.rb', line 955

def gitaly_operation_client
  @gitaly_operation_client ||= Gitlab::GitalyClient::OperationService.new(self)
end

#gitaly_ref_clientObject


943
944
945
# File 'lib/gitlab/git/repository.rb', line 943

def gitaly_ref_client
  @gitaly_ref_client ||= Gitlab::GitalyClient::RefService.new(self)
end

#gitaly_remote_clientObject


959
960
961
# File 'lib/gitlab/git/repository.rb', line 959

def gitaly_remote_client
  @gitaly_remote_client ||= Gitlab::GitalyClient::RemoteService.new(self)
end

#gitaly_repositoryObject


939
940
941
# File 'lib/gitlab/git/repository.rb', line 939

def gitaly_repository
  Gitlab::GitalyClient::Util.repository(@storage, @relative_path, @gl_repository, @gl_project_path)
end

#gitaly_repository_clientObject


951
952
953
# File 'lib/gitlab/git/repository.rb', line 951

def gitaly_repository_client
  @gitaly_repository_client ||= Gitlab::GitalyClient::RepositoryService.new(self)
end

#gitattribute(path, name) ⇒ Object


755
756
757
# File 'lib/gitlab/git/repository.rb', line 755

def gitattribute(path, name)
  attributes(path)[name]
end

#has_local_branches?Boolean Also known as: has_visible_content?

Returns:

  • (Boolean)

177
178
179
180
181
# File 'lib/gitlab/git/repository.rb', line 177

def has_local_branches?
  strong_memoize(:has_local_branches) do
    uncached_has_local_branches?
  end
end

#hashObject


78
79
80
# File 'lib/gitlab/git/repository.rb', line 78

def hash
  [self.class, storage, relative_path].hash
end

#import_repository(url, http_authorization_header: '', mirror: false) ⇒ Object

Raises:

  • (ArgumentError)

844
845
846
847
848
849
850
# File 'lib/gitlab/git/repository.rb', line 844

def import_repository(url, http_authorization_header: '', mirror: false)
  raise ArgumentError, "don't use disk paths with import_repository: #{url.inspect}" if url.start_with?('.', '/')

  wrapped_gitaly_errors do
    gitaly_repository_client.import_repository(url, http_authorization_header: http_authorization_header, mirror: mirror)
  end
end

#info_attributesObject


741
742
743
744
745
746
# File 'lib/gitlab/git/repository.rb', line 741

def info_attributes
  return @info_attributes if @info_attributes

  content = gitaly_repository_client.info_attributes
  @info_attributes = AttributesParser.new(content)
end

#languages(ref = nil) ⇒ Object


771
772
773
774
775
# File 'lib/gitlab/git/repository.rb', line 771

def languages(ref = nil)
  wrapped_gitaly_errors do
    gitaly_commit_client.languages(ref)
  end
end

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


1033
1034
1035
1036
1037
# File 'lib/gitlab/git/repository.rb', line 1033

def last_commit_for_path(sha, path, literal_pathspec: false)
  wrapped_gitaly_errors do
    gitaly_commit_client.last_commit_for_path(sha, path, literal_pathspec: literal_pathspec)
  end
end

#license_short_nameObject


777
778
779
780
781
# File 'lib/gitlab/git/repository.rb', line 777

def license_short_name
  wrapped_gitaly_errors do
    gitaly_repository_client.license_short_name
  end
end

#list_commits_by_ref_name(refs) ⇒ Object


1027
1028
1029
1030
1031
# File 'lib/gitlab/git/repository.rb', line 1027

def list_commits_by_ref_name(refs)
  wrapped_gitaly_errors do
    gitaly_commit_client.list_commits_by_ref_name(refs)
  end
end

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


1021
1022
1023
1024
1025
# File 'lib/gitlab/git/repository.rb', line 1021

def list_last_commits_for_tree(sha, path, offset: 0, limit: 25, literal_pathspec: false)
  wrapped_gitaly_errors do
    gitaly_commit_client.list_last_commits_for_tree(sha, path, offset: offset, limit: limit, literal_pathspec: literal_pathspec)
  end
end

#list_refsObject


803
804
805
806
807
# File 'lib/gitlab/git/repository.rb', line 803

def list_refs
  wrapped_gitaly_errors do
    gitaly_ref_client.list_refs
  end
end

#local_branches(sort_by: nil, pagination_params: nil) ⇒ Object


140
141
142
143
144
# File 'lib/gitlab/git/repository.rb', line 140

def local_branches(sort_by: nil, pagination_params: nil)
  wrapped_gitaly_errors do
    gitaly_ref_client.local_branches(sort_by: sort_by, pagination_params: pagination_params)
  end
end

#log(options) ⇒ Object

Build an array of commits.

Usage.

repo.log(
  ref: 'master',
  path: 'app/models',
  limit: 10,
  offset: 5,
  after: Time.new(2016, 4, 21, 14, 32, 10)
)

343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
# File 'lib/gitlab/git/repository.rb', line 343

def log(options)
  default_options = {
    limit: 10,
    offset: 0,
    path: nil,
    author: nil,
    follow: false,
    skip_merges: false,
    after: nil,
    before: nil,
    all: false
  }

  options = default_options.merge(options)
  options[:offset] ||= 0

  limit = options[:limit]
  if limit == 0 || !limit.is_a?(Integer)
    raise ArgumentError, "invalid Repository#log limit: #{limit.inspect}"
  end

  wrapped_gitaly_errors do
    gitaly_commit_client.find_commits(options)
  end
end

#ls_files(ref) ⇒ Object

Returns result like “git ls-files” , recursive and full file path

Ex.

repo.ls_files('master')

731
732
733
# File 'lib/gitlab/git/repository.rb', line 731

def ls_files(ref)
  gitaly_commit_client.ls_files(ref)
end

#merge(user, source_sha, target_branch, message, &block) ⇒ Object


636
637
638
639
640
# File 'lib/gitlab/git/repository.rb', line 636

def merge(user, source_sha, target_branch, message, &block)
  wrapped_gitaly_errors do
    gitaly_operation_client.user_merge_branch(user, source_sha, target_branch, message, &block)
  end
end

#merge_base(*commits) ⇒ Object

Returns the SHA of the most recent common ancestor of from and to


449
450
451
452
453
# File 'lib/gitlab/git/repository.rb', line 449

def merge_base(*commits)
  wrapped_gitaly_errors do
    gitaly_repository_client.find_merge_base(*commits)
  end
end

#merge_to_ref(user, **kwargs) ⇒ Object


630
631
632
633
634
# File 'lib/gitlab/git/repository.rb', line 630

def merge_to_ref(user, **kwargs)
  wrapped_gitaly_errors do
    gitaly_operation_client.user_merge_to_ref(user, **kwargs)
  end
end

#merged_branch_names(branch_names = []) ⇒ Object


460
461
462
463
464
465
466
467
468
469
470
471
472
# File 'lib/gitlab/git/repository.rb', line 460

def merged_branch_names(branch_names = [])
  return [] unless root_ref

  root_sha = find_branch(root_ref)&.target

  return [] unless root_sha

  branches = wrapped_gitaly_errors do
    gitaly_merged_branch_names(branch_names, root_sha)
  end

  Set.new(branches)
end

#multi_action(user, branch_name:, message:, actions:, author_email: nil, author_name: nil, start_branch_name: nil, start_sha: nil, start_repository: self, force: false) ⇒ Object

rubocop:disable Metrics/ParameterLists


910
911
912
913
914
915
916
917
918
919
920
921
# File 'lib/gitlab/git/repository.rb', line 910

def multi_action(
  user, branch_name:, message:, actions:,
  author_email: nil, author_name: nil,
  start_branch_name: nil, start_sha: nil, start_repository: self,
  force: false)

  wrapped_gitaly_errors do
    gitaly_operation_client.user_commit_files(user, branch_name,
        message, actions, author_email, author_name,
        start_branch_name, start_repository, force, start_sha)
  end
end

#new_blobs(newrevs, dynamic_timeout: nil) ⇒ Object


375
376
377
378
379
380
381
382
383
384
385
386
# File 'lib/gitlab/git/repository.rb', line 375

def new_blobs(newrevs, dynamic_timeout: nil)
  newrevs = Array.wrap(newrevs).reject { |rev| rev.blank? || rev == ::Gitlab::Git::BLANK_SHA }
  return [] if newrevs.empty?

  newrevs = newrevs.uniq.sort

  @new_blobs ||= Hash.new do |h, revs|
    h[revs] = blobs(['--not', '--all', '--not'] + newrevs, with_paths: true, dynamic_timeout: dynamic_timeout)
  end

  @new_blobs[newrevs]
end

#new_commits(newrevs, allow_quarantine: false) ⇒ Object


369
370
371
372
373
# File 'lib/gitlab/git/repository.rb', line 369

def new_commits(newrevs, allow_quarantine: false)
  wrapped_gitaly_errors do
    gitaly_commit_client.list_new_commits(Array.wrap(newrevs), allow_quarantine: allow_quarantine)
  end
end

#object_directory_sizeObject

Return git object directory size in bytes


329
330
331
# File 'lib/gitlab/git/repository.rb', line 329

def object_directory_size
  gitaly_repository_client.get_object_directory_size.to_f * 1024
end

#pathObject

This method will be removed when Gitaly reaches v1.1.


83
84
85
86
87
# File 'lib/gitlab/git/repository.rb', line 83

def path
  File.join(
    Gitlab.config.repositories.storages[@storage].legacy_disk_path, @relative_path
  )
end

#praefect_info_clientObject


971
972
973
# File 'lib/gitlab/git/repository.rb', line 971

def praefect_info_client
  @praefect_info_client ||= Gitlab::GitalyClient::PraefectInfoService.new(self)
end

#raw_changes_between(old_rev, new_rev) ⇒ Object

old_rev and new_rev are commit ID's the result of this method is an array of Gitlab::Git::RawDiffChange


430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
# File 'lib/gitlab/git/repository.rb', line 430

def raw_changes_between(old_rev, new_rev)
  @raw_changes_between ||= {}

  @raw_changes_between[[old_rev, new_rev]] ||=
    begin
      return [] if new_rev.blank? || new_rev == Gitlab::Git::BLANK_SHA

      wrapped_gitaly_errors do
        gitaly_repository_client.raw_changes_between(old_rev, new_rev)
          .each_with_object([]) do |msg, arr|
          msg.raw_changes.each { |change| arr << ::Gitlab::Git::RawDiffChange.new(change) }
        end
      end
    end
rescue ArgumentError => e
  raise Gitlab::Git::Repository::GitError, e
end

#rebase(user, rebase_id, branch:, branch_sha:, remote_repository:, remote_branch:, push_options: [], &block) ⇒ Object


880
881
882
883
884
885
886
887
888
889
890
891
892
893
# File 'lib/gitlab/git/repository.rb', line 880

def rebase(user, rebase_id, branch:, branch_sha:, remote_repository:, remote_branch:, push_options: [], &block)
  wrapped_gitaly_errors do
    gitaly_operation_client.rebase(
      user,
      rebase_id,
      branch: branch,
      branch_sha: branch_sha,
      remote_repository: remote_repository,
      remote_branch: remote_branch,
      push_options: push_options,
      &block
    )
  end
end

#ref_exists?(ref_name) ⇒ Boolean

Returns true if the given ref name exists

Ref names must start with `refs/`.

Returns:

  • (Boolean)

215
216
217
218
219
# File 'lib/gitlab/git/repository.rb', line 215

def ref_exists?(ref_name)
  wrapped_gitaly_errors do
    gitaly_ref_exists?(ref_name)
  end
end

#ref_namesObject

Returns an Array of branch and tag names


240
241
242
# File 'lib/gitlab/git/repository.rb', line 240

def ref_names
  branch_names + tag_names
end

#refs_by_oid(oid:, limit: 0) ⇒ Object

Returns matching refs for OID

Limit of 0 means there is no limit.


530
531
532
533
534
535
536
# File 'lib/gitlab/git/repository.rb', line 530

def refs_by_oid(oid:, limit: 0)
  wrapped_gitaly_errors do
    gitaly_ref_client.find_refs_by_oid(oid: oid, limit: limit)
  end
rescue CommandError, TypeError, NoRepository
  nil
end

#refs_hashObject

Get refs hash which key is the commit id and value is a Gitlab::Git::Tag or Gitlab::Git::Branch Note that both inherit from Gitlab::Git::Ref


513
514
515
516
517
518
519
520
521
522
523
524
525
# File 'lib/gitlab/git/repository.rb', line 513

def refs_hash
  return @refs_hash if @refs_hash

  @refs_hash = Hash.new { |h, k| h[k] = [] }

  (tags + branches).each do |ref|
    next unless ref.target && ref.name && ref.dereferenced_target&.id

    @refs_hash[ref.dereferenced_target.id] << ref.name
  end

  @refs_hash
end

#removeObject


159
160
161
162
163
164
165
# File 'lib/gitlab/git/repository.rb', line 159

def remove
  wrapped_gitaly_errors do
    gitaly_repository_client.remove
  end
rescue NoRepository
  nil
end

#rename(new_relative_path) ⇒ Object


153
154
155
156
157
# File 'lib/gitlab/git/repository.rb', line 153

def rename(new_relative_path)
  wrapped_gitaly_errors do
    gitaly_repository_client.rename(new_relative_path)
  end
end

#replicasObject


1048
1049
1050
1051
1052
# File 'lib/gitlab/git/repository.rb', line 1048

def replicas
  wrapped_gitaly_errors do
    praefect_info_client.replicas
  end
end

#replicate(source_repository) ⇒ Object


167
168
169
170
171
# File 'lib/gitlab/git/repository.rb', line 167

def replicate(source_repository)
  wrapped_gitaly_errors do
    gitaly_repository_client.replicate(source_repository)
  end
end

#revert(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:, dry_run: false) ⇒ Object


648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
# File 'lib/gitlab/git/repository.rb', line 648

def revert(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:, dry_run: false)
  args = {
    user: user,
    commit: commit,
    branch_name: branch_name,
    message: message,
    start_branch_name: start_branch_name,
    start_repository: start_repository,
    dry_run: dry_run
  }

  wrapped_gitaly_errors do
    gitaly_operation_client.user_revert(**args)
  end
end

#rm_branch(branch_name, user:) ⇒ Object


618
619
620
621
622
# File 'lib/gitlab/git/repository.rb', line 618

def rm_branch(branch_name, user:)
  wrapped_gitaly_errors do
    gitaly_operation_client.user_delete_branch(branch_name, user)
  end
end

#rm_tag(tag_name, user:) ⇒ Object


624
625
626
627
628
# File 'lib/gitlab/git/repository.rb', line 624

def rm_tag(tag_name, user:)
  wrapped_gitaly_errors do
    gitaly_operation_client.rm_tag(tag_name, user)
  end
end

#root_refObject

Default branch in the repository


90
91
92
93
94
95
96
# File 'lib/gitlab/git/repository.rb', line 90

def root_ref
  gitaly_ref_client.default_branch_name
rescue GRPC::NotFound => e
  raise NoRepository, e.message
rescue GRPC::Unknown => e
  raise Gitlab::Git::CommandError, e.message
end

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


983
984
985
986
987
988
989
990
# File 'lib/gitlab/git/repository.rb', line 983

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

  safe_query = Regexp.escape(query)
  ref ||= root_ref

  gitaly_repository_client.search_files_by_content(ref, safe_query, options)
end

#search_files_by_name(query, ref) ⇒ Object


1000
1001
1002
1003
1004
1005
1006
1007
# File 'lib/gitlab/git/repository.rb', line 1000

def search_files_by_name(query, ref)
  safe_query = Regexp.escape(query.sub(%r{^/*}, ""))
  ref ||= root_ref

  return [] if empty? || safe_query.blank?

  gitaly_repository_client.search_files_by_name(ref, safe_query)
end

#search_files_by_regexp(filter, ref = 'HEAD') ⇒ Object


1009
1010
1011
# File 'lib/gitlab/git/repository.rb', line 1009

def search_files_by_regexp(filter, ref = 'HEAD')
  gitaly_repository_client.search_files_by_regexp(ref, filter)
end

#set_full_path(full_path:) ⇒ Object

rubocop:enable Metrics/ParameterLists

Raises:


924
925
926
927
928
929
930
931
# File 'lib/gitlab/git/repository.rb', line 924

def set_full_path(full_path:)
  return unless full_path.present?

  # This guard avoids Gitaly log/error spam
  raise NoRepository, 'repository does not exist' unless exists?

  gitaly_repository_client.set_full_path(full_path)
end

#sizeObject

Return repo size in megabytes


322
323
324
325
326
# File 'lib/gitlab/git/repository.rb', line 322

def size
  size = gitaly_repository_client.repository_size

  (size.to_f / 1024).round(2)
end

#squash(user, start_sha:, end_sha:, author:, message:) ⇒ Object


895
896
897
898
899
# File 'lib/gitlab/git/repository.rb', line 895

def squash(user, start_sha:, end_sha:, author:, message:)
  wrapped_gitaly_errors do
    gitaly_operation_client.user_squash(user, start_sha, end_sha, author, message)
  end
end

#submodule_url_for(ref, path) ⇒ Object

Returns url for submodule

Ex.

@repository.submodule_url_for('master', 'rack')
# => [email protected]:rack.git

544
545
546
547
548
# File 'lib/gitlab/git/repository.rb', line 544

def submodule_url_for(ref, path)
  wrapped_gitaly_errors do
    gitaly_submodule_url_for(ref, path)
  end
end

#submodule_urls_for(ref) ⇒ Object

Returns path to url mappings for submodules

Ex.

@repository.submodule_urls_for('master')
# => { 'rack' => '[email protected]:rack.git' }

556
557
558
559
560
# File 'lib/gitlab/git/repository.rb', line 556

def submodule_urls_for(ref)
  wrapped_gitaly_errors do
    gitaly_submodule_urls_for(ref)
  end
end

#tag_countObject

Returns the number of valid tags


191
192
193
194
195
# File 'lib/gitlab/git/repository.rb', line 191

def tag_count
  wrapped_gitaly_errors do
    gitaly_ref_client.count_tag_names
  end
end

#tag_exists?(name) ⇒ Boolean

Returns true if the given tag exists

name - The name of the tag as a String.

Returns:

  • (Boolean)

224
225
226
227
228
# File 'lib/gitlab/git/repository.rb', line 224

def tag_exists?(name)
  wrapped_gitaly_errors do
    gitaly_ref_exists?("refs/tags/#{name}")
  end
end

#tag_namesObject

Returns an Array of tag names


198
199
200
201
202
# File 'lib/gitlab/git/repository.rb', line 198

def tag_names
  wrapped_gitaly_errors do
    gitaly_ref_client.tag_names
  end
end

#tag_names_contains_sha(sha) ⇒ Object


979
980
981
# File 'lib/gitlab/git/repository.rb', line 979

def tag_names_contains_sha(sha)
  gitaly_ref_client.tag_names_contains_sha(sha)
end

#tags(sort_by: nil, pagination_params: nil) ⇒ Object

Returns an Array of Tags


206
207
208
209
210
# File 'lib/gitlab/git/repository.rb', line 206

def tags(sort_by: nil, pagination_params: nil)
  wrapped_gitaly_errors do
    gitaly_ref_client.tags(sort_by: sort_by, pagination_params: pagination_params)
  end
end

#to_sObject


68
69
70
# File 'lib/gitlab/git/repository.rb', line 68

def to_s
  "<#{self.class.name}: #{self.gl_project_path}>"
end

#update_branch(branch_name, user:, newrev:, oldrev:) ⇒ Object


612
613
614
615
616
# File 'lib/gitlab/git/repository.rb', line 612

def update_branch(branch_name, user:, newrev:, oldrev:)
  wrapped_gitaly_errors do
    gitaly_operation_client.user_update_branch(branch_name, user, newrev, oldrev)
  end
end

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


680
681
682
683
684
685
686
687
688
689
690
691
692
# File 'lib/gitlab/git/repository.rb', line 680

def update_submodule(user:, submodule:, commit_sha:, message:, branch:)
  args = {
    user: user,
    submodule: submodule,
    commit_sha: commit_sha,
    branch: branch,
    message: message
  }

  wrapped_gitaly_errors do
    gitaly_operation_client.user_update_submodule(**args)
  end
end

#write_ref(ref_path, ref, old_ref: nil) ⇒ Object


795
796
797
798
799
800
801
# File 'lib/gitlab/git/repository.rb', line 795

def write_ref(ref_path, ref, old_ref: nil)
  ref_path = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{ref_path}" unless ref_path.start_with?("refs/") || ref_path == "HEAD"

  wrapped_gitaly_errors do
    gitaly_repository_client.write_ref(ref_path, ref, old_ref)
  end
end