Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob Vosmaer (GitLab) <jacob@gitlab.com>2018-01-05 19:38:05 +0300
committerJacob Vosmaer (GitLab) <jacob@gitlab.com>2018-01-05 19:38:05 +0300
commit351b3c31e90c11218e86202b0f5851f498a9cd14 (patch)
tree94a99fbb9c4e16a8879a0ddf34548b7bd067305a
parenta60a85841bc0187c6aa059359aba80d712a2c627 (diff)
parent7a95fc97a631484d708755368aab952a3e494c2c (diff)
Merge branch 'update-gitlab-git' into 'master'
Update vendored gitlab_git to 6eeb69fc9a2 See merge request gitlab-org/gitaly!514
-rw-r--r--CHANGELOG.md2
-rwxr-xr-x_support/vendor-gitlab-git2
-rw-r--r--ruby/lib/gitaly_server/conflicts_service.rb6
-rw-r--r--ruby/lib/gitlab/config.rb50
-rw-r--r--ruby/lib/gitlab/git.rb49
-rw-r--r--ruby/vendor/gitlab_git/REVISION2
-rw-r--r--ruby/vendor/gitlab_git/lib/gitlab/encoding_helper.rb36
-rw-r--r--ruby/vendor/gitlab_git/lib/gitlab/git.rb2
-rw-r--r--ruby/vendor/gitlab_git/lib/gitlab/git/blob.rb31
-rw-r--r--ruby/vendor/gitlab_git/lib/gitlab/git/commit_stats.rb9
-rw-r--r--ruby/vendor/gitlab_git/lib/gitlab/git/conflict/file.rb4
-rw-r--r--ruby/vendor/gitlab_git/lib/gitlab/git/conflict/resolution.rb15
-rw-r--r--ruby/vendor/gitlab_git/lib/gitlab/git/conflict/resolver.rb82
-rw-r--r--ruby/vendor/gitlab_git/lib/gitlab/git/gitlab_projects.rb66
-rw-r--r--ruby/vendor/gitlab_git/lib/gitlab/git/operation_service.rb5
-rw-r--r--ruby/vendor/gitlab_git/lib/gitlab/git/remote_mirror.rb75
-rw-r--r--ruby/vendor/gitlab_git/lib/gitlab/git/repository.rb110
-rw-r--r--ruby/vendor/gitlab_git/lib/gitlab/utils/strong_memoize.rb41
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