diff options
author | Jacob Vosmaer (GitLab) <jacob@gitlab.com> | 2018-01-05 19:38:05 +0300 |
---|---|---|
committer | Jacob Vosmaer (GitLab) <jacob@gitlab.com> | 2018-01-05 19:38:05 +0300 |
commit | 351b3c31e90c11218e86202b0f5851f498a9cd14 (patch) | |
tree | 94a99fbb9c4e16a8879a0ddf34548b7bd067305a | |
parent | a60a85841bc0187c6aa059359aba80d712a2c627 (diff) | |
parent | 7a95fc97a631484d708755368aab952a3e494c2c (diff) |
Merge branch 'update-gitlab-git' into 'master'
Update vendored gitlab_git to 6eeb69fc9a2
See merge request gitlab-org/gitaly!514
18 files changed, 443 insertions, 144 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bd62f118..ffb2ee7e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ UNRELEASED +- Update vendored gitlab_git to 6eeb69fc9a2 + https://gitlab.com/gitlab-org/gitaly/merge_requests/514 - Add support for MergedBranches in FindAllBranches RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/510 diff --git a/_support/vendor-gitlab-git b/_support/vendor-gitlab-git index f8f18f7cc..8baa45921 100755 --- a/_support/vendor-gitlab-git +++ b/_support/vendor-gitlab-git @@ -1,7 +1,7 @@ #!/usr/bin/env ruby # These files and directories of gitlab-ce will be vendored -FILE_LIST = %w[lib/gitlab/git.rb lib/gitlab/git lib/gitlab/encoding_helper.rb].freeze +FILE_LIST = %w[lib/gitlab/git.rb lib/gitlab/git lib/gitlab/encoding_helper.rb lib/gitlab/utils/strong_memoize.rb].freeze REMOTE = 'https://gitlab.com/gitlab-org/gitlab-ce'.freeze diff --git a/ruby/lib/gitaly_server/conflicts_service.rb b/ruby/lib/gitaly_server/conflicts_service.rb index 0fbea42e8..2a7f95880 100644 --- a/ruby/lib/gitaly_server/conflicts_service.rb +++ b/ruby/lib/gitaly_server/conflicts_service.rb @@ -63,12 +63,12 @@ module GitalyServer files = JSON.parse(files_json).map(&:with_indifferent_access) begin + resolution = Gitlab::Git::Conflict::Resolution.new(user, files, header.commit_message.dup) params = { source_branch: header.source_branch, - target_branch: header.target_branch, - commit_message: header.commit_message.dup + target_branch: header.target_branch } - resolver.resolve_conflicts(repo, user, files, params) + resolver.resolve_conflicts(repo, resolution, params) Gitaly::ResolveConflictsResponse.new rescue Gitlab::Git::Conflict::Resolver::ResolutionError => e diff --git a/ruby/lib/gitlab/config.rb b/ruby/lib/gitlab/config.rb new file mode 100644 index 000000000..46ca5d096 --- /dev/null +++ b/ruby/lib/gitlab/config.rb @@ -0,0 +1,50 @@ +module Gitlab + # Config lets Gitlab::Git do mock config lookups. + class Config + class Git + def bin_path + ENV['GITALY_RUBY_GIT_BIN_PATH'] + end + + def write_buffer_size + @write_buffer_size ||= ENV['GITALY_RUBY_WRITE_BUFFER_SIZE'].to_i + end + end + + class GitlabShell + def path + ENV['GITALY_RUBY_GITLAB_SHELL_PATH'] + end + + def hooks_path + File.join(path, 'hooks') + end + + def git_timeout + 10800 # TODO make this configurable or eliminate otherwise https://gitlab.com/gitlab-org/gitaly/issues/885 + end + end + + class Gitaly + def client_path + ENV['GITALY_RUBY_GITALY_BIN_DIR'] + end + end + + def git + Git.new + end + + def gitlab_shell + GitlabShell.new + end + + def gitaly + Gitaly.new + end + end + + def self.config + Config.new + end +end diff --git a/ruby/lib/gitlab/git.rb b/ruby/lib/gitlab/git.rb index eafb0df76..e2f170578 100644 --- a/ruby/lib/gitlab/git.rb +++ b/ruby/lib/gitlab/git.rb @@ -15,11 +15,13 @@ require_relative 'gitaly_client.rb' require_relative 'git_logger.rb' require_relative 'rails_logger.rb' require_relative 'gollum.rb' +require_relative 'config.rb' vendor_gitlab_git = '../../vendor/gitlab_git/' # Some later requires are order-sensitive. Manually require whatever we need. require_relative File.join(vendor_gitlab_git, 'lib/gitlab/encoding_helper.rb') +require_relative File.join(vendor_gitlab_git, 'lib/gitlab/utils/strong_memoize.rb') require_relative File.join(vendor_gitlab_git, 'lib/gitlab/git.rb') require_relative File.join(vendor_gitlab_git, 'lib/gitlab/git/popen.rb') require_relative File.join(vendor_gitlab_git, 'lib/gitlab/git/ref.rb') @@ -37,53 +39,6 @@ end require_relative 'git/gitaly_remote_repository.rb' module Gitlab - # Config lets Gitlab::Git do mock config lookups. - class Config - class Git - def bin_path - ENV['GITALY_RUBY_GIT_BIN_PATH'] - end - - def write_buffer_size - @write_buffer_size ||= ENV['GITALY_RUBY_WRITE_BUFFER_SIZE'].to_i - end - end - - class GitlabShell - def path - ENV['GITALY_RUBY_GITLAB_SHELL_PATH'] - end - - def hooks_path - File.join(path, 'hooks') - end - end - - class Gitaly - def client_path - ENV['GITALY_RUBY_GITALY_BIN_DIR'] - end - end - - def git - Git.new - end - - def gitlab_shell - GitlabShell.new - end - - def gitaly - Gitaly.new - end - end - - def self.config - Config.new - end -end - -module Gitlab module Git class Repository def self.from_gitaly(gitaly_repository, call) diff --git a/ruby/vendor/gitlab_git/REVISION b/ruby/vendor/gitlab_git/REVISION index 3ba83b42e..91e235408 100644 --- a/ruby/vendor/gitlab_git/REVISION +++ b/ruby/vendor/gitlab_git/REVISION @@ -1 +1 @@ -b98c69470f52185117fcdb5e28096826b32363ca +6eeb69fc9a216bd1874cba85214af5b1da1a46d0 diff --git a/ruby/vendor/gitlab_git/lib/gitlab/encoding_helper.rb b/ruby/vendor/gitlab_git/lib/gitlab/encoding_helper.rb index 582028493..c0edcabc6 100644 --- a/ruby/vendor/gitlab_git/lib/gitlab/encoding_helper.rb +++ b/ruby/vendor/gitlab_git/lib/gitlab/encoding_helper.rb @@ -14,14 +14,7 @@ module Gitlab ENCODING_CONFIDENCE_THRESHOLD = 50 def encode!(message) - return nil unless message.respond_to?(:force_encoding) - return message if message.encoding == Encoding::UTF_8 && message.valid_encoding? - - if message.respond_to?(:frozen?) && message.frozen? - message = message.dup - end - - message.force_encoding("UTF-8") + message = force_encode_utf8(message) return message if message.valid_encoding? # return message if message type is binary @@ -35,6 +28,8 @@ module Gitlab # encode and clean the bad chars message.replace clean(message) + rescue ArgumentError + return nil rescue encoding = detect ? detect[:encoding] : "unknown" "--broken encoding: #{encoding}" @@ -54,8 +49,8 @@ module Gitlab end def encode_utf8(message) - return nil unless message.is_a?(String) - return message if message.encoding == Encoding::UTF_8 && message.valid_encoding? + message = force_encode_utf8(message) + return message if message.valid_encoding? detect = CharlockHolmes::EncodingDetector.detect(message) if detect && detect[:encoding] @@ -69,10 +64,31 @@ module Gitlab else clean(message) end + rescue ArgumentError + return nil + end + + def encode_binary(s) + return "" if s.nil? + + s.dup.force_encoding(Encoding::ASCII_8BIT) + end + + def binary_stringio(s) + StringIO.new(s || '').tap { |io| io.set_encoding(Encoding::ASCII_8BIT) } end private + def force_encode_utf8(message) + raise ArgumentError unless message.respond_to?(:force_encoding) + return message if message.encoding == Encoding::UTF_8 && message.valid_encoding? + + message = message.dup if message.respond_to?(:frozen?) && message.frozen? + + message.force_encoding("UTF-8") + end + def clean(message) message.encode("UTF-16BE", undef: :replace, invalid: :replace, replace: "") .encode("UTF-8") diff --git a/ruby/vendor/gitlab_git/lib/gitlab/git.rb b/ruby/vendor/gitlab_git/lib/gitlab/git.rb index 1f7c35caf..71647099f 100644 --- a/ruby/vendor/gitlab_git/lib/gitlab/git.rb +++ b/ruby/vendor/gitlab_git/lib/gitlab/git.rb @@ -11,7 +11,7 @@ module Gitlab include Gitlab::EncodingHelper def ref_name(ref) - encode_utf8(ref).sub(/\Arefs\/(tags|heads|remotes)\//, '') + encode!(ref).sub(/\Arefs\/(tags|heads|remotes)\//, '') end def branch_name(ref) diff --git a/ruby/vendor/gitlab_git/lib/gitlab/git/blob.rb b/ruby/vendor/gitlab_git/lib/gitlab/git/blob.rb index 228d97a87..a1755143a 100644 --- a/ruby/vendor/gitlab_git/lib/gitlab/git/blob.rb +++ b/ruby/vendor/gitlab_git/lib/gitlab/git/blob.rb @@ -50,10 +50,19 @@ module Gitlab # to the caller to limit the number of blobs and blob_size_limit. # # Gitaly migration issue: https://gitlab.com/gitlab-org/gitaly/issues/798 - def batch(repository, blob_references, blob_size_limit: nil) - blob_size_limit ||= MAX_DATA_DISPLAY_SIZE - blob_references.map do |sha, path| - find_by_rugged(repository, sha, path, limit: blob_size_limit) + def batch(repository, blob_references, blob_size_limit: MAX_DATA_DISPLAY_SIZE) + Gitlab::GitalyClient.migrate(:list_blobs_by_sha_path) do |is_enabled| + if is_enabled + Gitlab::GitalyClient.allow_n_plus_1_calls do + blob_references.map do |sha, path| + find_by_gitaly(repository, sha, path, limit: blob_size_limit) + end + end + else + blob_references.map do |sha, path| + find_by_rugged(repository, sha, path, limit: blob_size_limit) + end + end end end @@ -122,13 +131,23 @@ module Gitlab ) end - def find_by_gitaly(repository, sha, path) + def find_by_gitaly(repository, sha, path, limit: MAX_DATA_DISPLAY_SIZE) path = path.sub(/\A\/*/, '') path = '/' if path.empty? name = File.basename(path) - entry = Gitlab::GitalyClient::CommitService.new(repository).tree_entry(sha, path, MAX_DATA_DISPLAY_SIZE) + + # Gitaly will think that setting the limit to 0 means unlimited, while + # the client might only need the metadata and thus set the limit to 0. + # In this method we'll then set the limit to 1, but clear the byte of data + # that we got back so for the outside world it looks like the limit was + # actually 0. + req_limit = limit == 0 ? 1 : limit + + entry = Gitlab::GitalyClient::CommitService.new(repository).tree_entry(sha, path, req_limit) return unless entry + entry.data = "" if limit == 0 + case entry.type when :COMMIT new( diff --git a/ruby/vendor/gitlab_git/lib/gitlab/git/commit_stats.rb b/ruby/vendor/gitlab_git/lib/gitlab/git/commit_stats.rb index 6bf49a0af..8463b1eb7 100644 --- a/ruby/vendor/gitlab_git/lib/gitlab/git/commit_stats.rb +++ b/ruby/vendor/gitlab_git/lib/gitlab/git/commit_stats.rb @@ -34,13 +34,8 @@ module Gitlab def rugged_stats(commit) diff = commit.rugged_diff_from_parent - - diff.each_patch do |p| - # TODO: Use the new Rugged convenience methods when they're released - @additions += p.stat[0] - @deletions += p.stat[1] - @total += p.changes - end + _files_changed, @additions, @deletions = diff.stat + @total = @additions + @deletions end end end diff --git a/ruby/vendor/gitlab_git/lib/gitlab/git/conflict/file.rb b/ruby/vendor/gitlab_git/lib/gitlab/git/conflict/file.rb index b2a625e08..2a9cf10a0 100644 --- a/ruby/vendor/gitlab_git/lib/gitlab/git/conflict/file.rb +++ b/ruby/vendor/gitlab_git/lib/gitlab/git/conflict/file.rb @@ -2,7 +2,9 @@ module Gitlab module Git module Conflict class File - attr_reader :content, :their_path, :our_path, :our_mode, :repository, :commit_oid + attr_reader :their_path, :our_path, :our_mode, :repository, :commit_oid + + attr_accessor :content def initialize(repository, commit_oid, conflict, content) @repository = repository diff --git a/ruby/vendor/gitlab_git/lib/gitlab/git/conflict/resolution.rb b/ruby/vendor/gitlab_git/lib/gitlab/git/conflict/resolution.rb new file mode 100644 index 000000000..ab9be683e --- /dev/null +++ b/ruby/vendor/gitlab_git/lib/gitlab/git/conflict/resolution.rb @@ -0,0 +1,15 @@ +module Gitlab + module Git + module Conflict + class Resolution + attr_reader :user, :files, :commit_message + + def initialize(user, files, commit_message) + @user = user + @files = files + @commit_message = commit_message + end + end + end + end +end diff --git a/ruby/vendor/gitlab_git/lib/gitlab/git/conflict/resolver.rb b/ruby/vendor/gitlab_git/lib/gitlab/git/conflict/resolver.rb index 03e5c0fcd..74c9874d5 100644 --- a/ruby/vendor/gitlab_git/lib/gitlab/git/conflict/resolver.rb +++ b/ruby/vendor/gitlab_git/lib/gitlab/git/conflict/resolver.rb @@ -13,37 +13,27 @@ module Gitlab def conflicts @conflicts ||= begin - target_index = @target_repository.rugged.merge_commits(@our_commit_oid, @their_commit_oid) - - # We don't need to do `with_repo_branch_commit` here, because the target - # project always fetches source refs when creating merge request diffs. - conflict_files(@target_repository, target_index) + @target_repository.gitaly_migrate(:conflicts_list_conflict_files) do |is_enabled| + if is_enabled + gitaly_conflicts_client(@target_repository).list_conflict_files + else + rugged_list_conflict_files + end + end end + rescue GRPC::FailedPrecondition => e + raise Gitlab::Git::Conflict::Resolver::ConflictSideMissing.new(e.message) + rescue Rugged::OdbError, GRPC::BadStatus => e + raise Gitlab::Git::CommandError.new(e) end - def resolve_conflicts(source_repository, user, files, source_branch:, target_branch:, commit_message:) - source_repository.with_repo_branch_commit(@target_repository, target_branch) do - index = source_repository.rugged.merge_commits(@our_commit_oid, @their_commit_oid) - conflicts = conflict_files(source_repository, index) - - files.each do |file_params| - conflict_file = conflict_for_path(conflicts, file_params[:old_path], file_params[:new_path]) - - write_resolved_file_to_index(source_repository, index, conflict_file, file_params) - end - - unless index.conflicts.empty? - missing_files = index.conflicts.map { |file| file[:ours][:path] } - - raise ResolutionError, "Missing resolutions for the following files: #{missing_files.join(', ')}" + def resolve_conflicts(source_repository, resolution, source_branch:, target_branch:) + source_repository.gitaly_migrate(:conflicts_resolve_conflicts) do |is_enabled| + if is_enabled + gitaly_conflicts_client(source_repository).resolve_conflicts(@target_repository, resolution, source_branch, target_branch) + else + rugged_resolve_conflicts(source_repository, resolution, source_branch, target_branch) end - - commit_params = { - message: commit_message, - parents: [@our_commit_oid, @their_commit_oid] - } - - source_repository.commit_index(user, source_branch, index, commit_params) end end @@ -68,6 +58,10 @@ module Gitlab end end + def gitaly_conflicts_client(repository) + repository.gitaly_conflicts_client(@our_commit_oid, @their_commit_oid) + end + def write_resolved_file_to_index(repository, index, file, params) if params[:sections] resolved_lines = file.resolve_lines(params[:sections]) @@ -84,6 +78,40 @@ module Gitlab index.add(path: our_path, oid: oid, mode: file.our_mode) index.conflict_remove(our_path) end + + def rugged_list_conflict_files + target_index = @target_repository.rugged.merge_commits(@our_commit_oid, @their_commit_oid) + + # We don't need to do `with_repo_branch_commit` here, because the target + # project always fetches source refs when creating merge request diffs. + conflict_files(@target_repository, target_index) + end + + def rugged_resolve_conflicts(source_repository, resolution, source_branch, target_branch) + source_repository.with_repo_branch_commit(@target_repository, target_branch) do + index = source_repository.rugged.merge_commits(@our_commit_oid, @their_commit_oid) + conflicts = conflict_files(source_repository, index) + + resolution.files.each do |file_params| + conflict_file = conflict_for_path(conflicts, file_params[:old_path], file_params[:new_path]) + + write_resolved_file_to_index(source_repository, index, conflict_file, file_params) + end + + unless index.conflicts.empty? + missing_files = index.conflicts.map { |file| file[:ours][:path] } + + raise ResolutionError, "Missing resolutions for the following files: #{missing_files.join(', ')}" + end + + commit_params = { + message: resolution.commit_message, + parents: [@our_commit_oid, @their_commit_oid] + } + + source_repository.commit_index(resolution.user, source_branch, index, commit_params) + end + end end end end diff --git a/ruby/vendor/gitlab_git/lib/gitlab/git/gitlab_projects.rb b/ruby/vendor/gitlab_git/lib/gitlab/git/gitlab_projects.rb index d948d7895..cba638c06 100644 --- a/ruby/vendor/gitlab_git/lib/gitlab/git/gitlab_projects.rb +++ b/ruby/vendor/gitlab_git/lib/gitlab/git/gitlab_projects.rb @@ -2,6 +2,9 @@ module Gitlab module Git class GitlabProjects include Gitlab::Git::Popen + include Gitlab::Utils::StrongMemoize + + ShardNameNotFoundError = Class.new(StandardError) # Absolute path to directory where repositories are stored. # Example: /home/git/repositories @@ -97,22 +100,13 @@ module Gitlab end def fork_repository(new_shard_path, new_repository_relative_path) - from_path = repository_absolute_path - to_path = File.join(new_shard_path, new_repository_relative_path) - - # The repository cannot already exist - if File.exist?(to_path) - logger.error "fork-repository failed: destination repository <#{to_path}> already exists." - return false + Gitlab::GitalyClient.migrate(:fork_repository) do |is_enabled| + if is_enabled + gitaly_fork_repository(new_shard_path, new_repository_relative_path) + else + git_fork_repository(new_shard_path, new_repository_relative_path) + end end - - # Ensure the namepsace / hashed storage directory exists - FileUtils.mkdir_p(File.dirname(to_path), mode: 0770) - - logger.info "Forking repository from <#{from_path}> to <#{to_path}>." - cmd = %W(git clone --bare --no-local -- #{from_path} #{to_path}) - - run(cmd, nil) && Gitlab::Git::Repository.create_hooks(to_path, global_hooks_path) end def fetch_remote(name, timeout, force:, tags:, ssh_key: nil, known_hosts: nil) @@ -253,6 +247,48 @@ module Gitlab known_hosts_file&.close! script&.close! end + + private + + def shard_name + strong_memoize(:shard_name) do + shard_name_from_shard_path(shard_path) + end + end + + def shard_name_from_shard_path(shard_path) + Gitlab.config.repositories.storages.find { |_, info| info['path'] == shard_path }&.first || + raise(ShardNameNotFoundError, "no shard found for path '#{shard_path}'") + end + + def git_fork_repository(new_shard_path, new_repository_relative_path) + from_path = repository_absolute_path + to_path = File.join(new_shard_path, new_repository_relative_path) + + # The repository cannot already exist + if File.exist?(to_path) + logger.error "fork-repository failed: destination repository <#{to_path}> already exists." + return false + end + + # Ensure the namepsace / hashed storage directory exists + FileUtils.mkdir_p(File.dirname(to_path), mode: 0770) + + logger.info "Forking repository from <#{from_path}> to <#{to_path}>." + cmd = %W(git clone --bare --no-local -- #{from_path} #{to_path}) + + run(cmd, nil) && Gitlab::Git::Repository.create_hooks(to_path, global_hooks_path) + end + + def gitaly_fork_repository(new_shard_path, new_repository_relative_path) + target_repository = Gitlab::Git::Repository.new(shard_name_from_shard_path(new_shard_path), new_repository_relative_path, nil) + raw_repository = Gitlab::Git::Repository.new(shard_name, repository_relative_path, nil) + + Gitlab::GitalyClient::RepositoryService.new(target_repository).fork_repository(raw_repository) + rescue GRPC::BadStatus => e + logger.error "fork-repository failed: #{e.message}" + false + end end end end diff --git a/ruby/vendor/gitlab_git/lib/gitlab/git/operation_service.rb b/ruby/vendor/gitlab_git/lib/gitlab/git/operation_service.rb index ef5bdbaf8..3fb0e2eed 100644 --- a/ruby/vendor/gitlab_git/lib/gitlab/git/operation_service.rb +++ b/ruby/vendor/gitlab_git/lib/gitlab/git/operation_service.rb @@ -97,6 +97,11 @@ module Gitlab end end + def update_branch(branch_name, newrev, oldrev) + ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name + update_ref_in_hooks(ref, newrev, oldrev) + end + private # Returns [newrev, should_run_after_create, should_run_after_create_branch] diff --git a/ruby/vendor/gitlab_git/lib/gitlab/git/remote_mirror.rb b/ruby/vendor/gitlab_git/lib/gitlab/git/remote_mirror.rb new file mode 100644 index 000000000..38e9d2a85 --- /dev/null +++ b/ruby/vendor/gitlab_git/lib/gitlab/git/remote_mirror.rb @@ -0,0 +1,75 @@ +module Gitlab + module Git + class RemoteMirror + def initialize(repository, ref_name) + @repository = repository + @ref_name = ref_name + end + + def update(only_branches_matching: [], only_tags_matching: []) + local_branches = refs_obj(@repository.local_branches, only_refs_matching: only_branches_matching) + remote_branches = refs_obj(@repository.remote_branches(@ref_name), only_refs_matching: only_branches_matching) + + updated_branches = changed_refs(local_branches, remote_branches) + push_branches(updated_branches.keys) if updated_branches.present? + + delete_refs(local_branches, remote_branches) + + local_tags = refs_obj(@repository.tags, only_refs_matching: only_tags_matching) + remote_tags = refs_obj(@repository.remote_tags(@ref_name), only_refs_matching: only_tags_matching) + + updated_tags = changed_refs(local_tags, remote_tags) + @repository.push_remote_branches(@ref_name, updated_tags.keys) if updated_tags.present? + + delete_refs(local_tags, remote_tags) + end + + private + + def refs_obj(refs, only_refs_matching: []) + refs.each_with_object({}) do |ref, refs| + next if only_refs_matching.present? && !only_refs_matching.include?(ref.name) + + refs[ref.name] = ref + end + end + + def changed_refs(local_refs, remote_refs) + local_refs.select do |ref_name, ref| + remote_ref = remote_refs[ref_name] + + remote_ref.nil? || ref.dereferenced_target != remote_ref.dereferenced_target + end + end + + def push_branches(branches) + default_branch, branches = branches.partition do |branch| + @repository.root_ref == branch + end + + # Push the default branch first so it works fine when remote mirror is empty. + branches.unshift(*default_branch) + + @repository.push_remote_branches(@ref_name, branches) + end + + def delete_refs(local_refs, remote_refs) + refs = refs_to_delete(local_refs, remote_refs) + + @repository.delete_remote_branches(@ref_name, refs.keys) if refs.present? + end + + def refs_to_delete(local_refs, remote_refs) + default_branch_id = @repository.commit.id + + remote_refs.select do |remote_ref_name, remote_ref| + next false if local_refs[remote_ref_name] # skip if branch or tag exist in local repo + + remote_ref_id = remote_ref.dereferenced_target.try(:id) + + remote_ref_id && @repository.rugged_is_ancestor?(remote_ref_id, default_branch_id) + end + end + end + end +end diff --git a/ruby/vendor/gitlab_git/lib/gitlab/git/repository.rb b/ruby/vendor/gitlab_git/lib/gitlab/git/repository.rb index 603323d04..17c05c44d 100644 --- a/ruby/vendor/gitlab_git/lib/gitlab/git/repository.rb +++ b/ruby/vendor/gitlab_git/lib/gitlab/git/repository.rb @@ -21,6 +21,7 @@ module Gitlab REBASE_WORKTREE_PREFIX = 'rebase'.freeze SQUASH_WORKTREE_PREFIX = 'squash'.freeze GITALY_INTERNAL_URL = 'ssh://gitaly/internal.git'.freeze + GITLAB_PROJECTS_TIMEOUT = Gitlab.config.gitlab_shell.git_timeout NoRepository = Class.new(StandardError) InvalidBlobName = Class.new(StandardError) @@ -83,7 +84,7 @@ module Gitlab # Rugged repo object attr_reader :rugged - attr_reader :storage, :gl_repository, :relative_path + attr_reader :gitlab_projects, :storage, :gl_repository, :relative_path # This initializer method is only used on the client side (gitlab-ce). # Gitaly-ruby uses a different initializer. @@ -93,6 +94,12 @@ module Gitlab @gl_repository = gl_repository storage_path = Gitlab.config.repositories.storages[@storage]['path'] + @gitlab_projects = Gitlab::Git::GitlabProjects.new( + storage_path, + relative_path, + global_hooks_path: Gitlab.config.gitlab_shell.hooks_path, + logger: Rails.logger + ) @path = File.join(storage_path, @relative_path) @name = @relative_path.split("/").last @attributes = Gitlab::Git::Attributes.new(path) @@ -126,7 +133,7 @@ module Gitlab end def exists? - Gitlab::GitalyClient.migrate(:repository_exists, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |enabled| + Gitlab::GitalyClient.migrate(:repository_exists) do |enabled| if enabled gitaly_repository_client.exists? else @@ -188,7 +195,7 @@ module Gitlab end def local_branches(sort_by: nil) - gitaly_migrate(:local_branches, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled| + gitaly_migrate(:local_branches) do |is_enabled| if is_enabled gitaly_ref_client.local_branches(sort_by: sort_by) else @@ -919,31 +926,23 @@ module Gitlab # If `mirror_refmap` is present the remote is set as mirror with that mapping def add_remote(remote_name, url, mirror_refmap: nil) - rugged.remotes.create(remote_name, url) - - set_remote_as_mirror(remote_name, refmap: mirror_refmap) if mirror_refmap - rescue Rugged::ConfigError - remote_update(remote_name, url: url) + gitaly_migrate(:remote_add_remote) do |is_enabled| + if is_enabled + gitaly_remote_client.add_remote(remote_name, url, mirror_refmap) + else + rugged_add_remote(remote_name, url, mirror_refmap) + end + end end def remove_remote(remote_name) - # When a remote is deleted all its remote refs are deleted too, but in - # the case of mirrors we map its refs (that would usualy go under - # [remote_name]/) to the top level namespace. We clean the mapping so - # those don't get deleted. - if rugged.config["remote.#{remote_name}.mirror"] - rugged.config.delete("remote.#{remote_name}.fetch") + gitaly_migrate(:remote_remove_remote) do |is_enabled| + if is_enabled + gitaly_remote_client.remove_remote(remote_name) + else + rugged_remove_remote(remote_name) + end end - - rugged.remotes.delete(remote_name) - true - rescue Rugged::ConfigError - false - end - - # Returns true if a remote exists. - def remote_exists?(name) - rugged.remotes[name].present? end # Update the specified remote using the values in the +options+ hash @@ -1220,9 +1219,16 @@ module Gitlab rebase_path = worktree_path(REBASE_WORKTREE_PREFIX, rebase_id) env = git_env_for_user(user) + if remote_repository.is_a?(RemoteRepository) + env.merge!(remote_repository.fetch_env) + remote_repo_path = GITALY_INTERNAL_URL + else + remote_repo_path = remote_repository.path + end + with_worktree(rebase_path, branch, env: env) do run_git!( - %W(pull --rebase #{remote_repository.path} #{remote_branch}), + %W(pull --rebase #{remote_repo_path} #{remote_branch}), chdir: rebase_path, env: env ) @@ -1274,6 +1280,24 @@ module Gitlab fresh_worktree?(worktree_path(SQUASH_WORKTREE_PREFIX, squash_id)) end + def push_remote_branches(remote_name, branch_names, forced: true) + success = @gitlab_projects.push_branches(remote_name, GITLAB_PROJECTS_TIMEOUT, forced, branch_names) + + success || gitlab_projects_error + end + + def delete_remote_branches(remote_name, branch_names) + success = @gitlab_projects.delete_remote_branches(remote_name, branch_names) + + success || gitlab_projects_error + end + + def delete_remote_branches(remote_name, branch_names) + success = @gitlab_projects.delete_remote_branches(remote_name, branch_names) + + success || gitlab_projects_error + end + def gitaly_repository Gitlab::GitalyClient::Util.repository(@storage, @relative_path, @gl_repository) end @@ -1298,6 +1322,14 @@ module Gitlab @gitaly_operation_client ||= Gitlab::GitalyClient::OperationService.new(self) end + def gitaly_remote_client + @gitaly_remote_client ||= Gitlab::GitalyClient::RemoteService.new(self) + end + + def gitaly_conflicts_client(our_commit_oid, their_commit_oid) + Gitlab::GitalyClient::ConflictsService.new(self, our_commit_oid, their_commit_oid) + end + def gitaly_migrate(method, status: Gitlab::GitalyClient::MigrationStatus::OPT_IN, &block) Gitlab::GitalyClient.migrate(method, status: status, &block) rescue GRPC::NotFound => e @@ -1665,6 +1697,7 @@ module Gitlab cmd = %W[#{Gitlab.config.git.bin_path} --git-dir=#{path} rev-list] cmd << "--after=#{options[:after].iso8601}" if options[:after] cmd << "--before=#{options[:before].iso8601}" if options[:before] + cmd << "--max-count=#{options[:max_count]}" if options[:max_count] cmd += %W[--count #{options[:ref]}] cmd += %W[-- #{options[:path]}] if options[:path].present? @@ -1917,9 +1950,36 @@ module Gitlab raise ArgumentError, 'Invalid merge source' end + def rugged_add_remote(remote_name, url, mirror_refmap) + rugged.remotes.create(remote_name, url) + + set_remote_as_mirror(remote_name, refmap: mirror_refmap) if mirror_refmap + rescue Rugged::ConfigError + remote_update(remote_name, url: url) + end + + def rugged_remove_remote(remote_name) + # When a remote is deleted all its remote refs are deleted too, but in + # the case of mirrors we map its refs (that would usualy go under + # [remote_name]/) to the top level namespace. We clean the mapping so + # those don't get deleted. + if rugged.config["remote.#{remote_name}.mirror"] + rugged.config.delete("remote.#{remote_name}.fetch") + end + + rugged.remotes.delete(remote_name) + true + rescue Rugged::ConfigError + false + end + def fetch_remote(remote_name = 'origin', env: nil) run_git(['fetch', remote_name], env: env).last.zero? end + + def gitlab_projects_error + raise CommandError, @gitlab_projects.output + end end end end diff --git a/ruby/vendor/gitlab_git/lib/gitlab/utils/strong_memoize.rb b/ruby/vendor/gitlab_git/lib/gitlab/utils/strong_memoize.rb new file mode 100644 index 000000000..fe091f461 --- /dev/null +++ b/ruby/vendor/gitlab_git/lib/gitlab/utils/strong_memoize.rb @@ -0,0 +1,41 @@ +module Gitlab + module Utils + module StrongMemoize + # Instead of writing patterns like this: + # + # def trigger_from_token + # return @trigger if defined?(@trigger) + # + # @trigger = Ci::Trigger.find_by_token(params[:token].to_s) + # end + # + # We could write it like: + # + # include Gitlab::Utils::StrongMemoize + # + # def trigger_from_token + # strong_memoize(:trigger) do + # Ci::Trigger.find_by_token(params[:token].to_s) + # end + # end + # + def strong_memoize(name) + if instance_variable_defined?(ivar(name)) + instance_variable_get(ivar(name)) + else + instance_variable_set(ivar(name), yield) + end + end + + def clear_memoization(name) + remove_instance_variable(ivar(name)) if instance_variable_defined?(ivar(name)) + end + + private + + def ivar(name) + "@#{name}" + end + end + end +end |