Merge branch 'feature/use-gitaly-for-commit-show' into 'master'
Use Gitaly for CommitController#show See merge request !9629
This commit is contained in:
commit
5409a3c5b7
11 changed files with 199 additions and 7 deletions
2
Gemfile
2
Gemfile
|
@ -352,4 +352,4 @@ gem 'vmstat', '~> 2.3.0'
|
||||||
gem 'sys-filesystem', '~> 1.1.6'
|
gem 'sys-filesystem', '~> 1.1.6'
|
||||||
|
|
||||||
# Gitaly GRPC client
|
# Gitaly GRPC client
|
||||||
gem 'gitaly', '~> 0.2.1'
|
gem 'gitaly', '~> 0.3.0'
|
||||||
|
|
|
@ -250,7 +250,7 @@ GEM
|
||||||
json
|
json
|
||||||
get_process_mem (0.2.0)
|
get_process_mem (0.2.0)
|
||||||
gherkin-ruby (0.3.2)
|
gherkin-ruby (0.3.2)
|
||||||
gitaly (0.2.1)
|
gitaly (0.3.0)
|
||||||
google-protobuf (~> 3.1)
|
google-protobuf (~> 3.1)
|
||||||
grpc (~> 1.0)
|
grpc (~> 1.0)
|
||||||
github-linguist (4.7.6)
|
github-linguist (4.7.6)
|
||||||
|
@ -896,7 +896,7 @@ DEPENDENCIES
|
||||||
fuubar (~> 2.0.0)
|
fuubar (~> 2.0.0)
|
||||||
gemnasium-gitlab-service (~> 0.2)
|
gemnasium-gitlab-service (~> 0.2)
|
||||||
gemojione (~> 3.0)
|
gemojione (~> 3.0)
|
||||||
gitaly (~> 0.2.1)
|
gitaly (~> 0.3.0)
|
||||||
github-linguist (~> 4.7.0)
|
github-linguist (~> 4.7.0)
|
||||||
gitlab-flowdock-git-hook (~> 1.0.1)
|
gitlab-flowdock-git-hook (~> 1.0.1)
|
||||||
gitlab-markup (~> 1.5.1)
|
gitlab-markup (~> 1.5.1)
|
||||||
|
|
|
@ -321,7 +321,14 @@ class Commit
|
||||||
end
|
end
|
||||||
|
|
||||||
def raw_diffs(*args)
|
def raw_diffs(*args)
|
||||||
raw.diffs(*args)
|
use_gitaly = Gitlab::GitalyClient.feature_enabled?(:commit_raw_diffs)
|
||||||
|
deltas_only = args.last.is_a?(Hash) && args.last[:deltas_only]
|
||||||
|
|
||||||
|
if use_gitaly && !deltas_only
|
||||||
|
Gitlab::GitalyClient::Commit.diff_from_parent(self, *args)
|
||||||
|
else
|
||||||
|
raw.diffs(*args)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def diffs(diff_options = nil)
|
def diffs(diff_options = nil)
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
title: Use Gitaly for CommitController#show
|
||||||
|
merge_request: 9629
|
||||||
|
author:
|
|
@ -176,9 +176,13 @@ module Gitlab
|
||||||
def initialize(raw_diff, collapse: false)
|
def initialize(raw_diff, collapse: false)
|
||||||
case raw_diff
|
case raw_diff
|
||||||
when Hash
|
when Hash
|
||||||
init_from_hash(raw_diff, collapse: collapse)
|
init_from_hash(raw_diff)
|
||||||
|
prune_diff_if_eligible(collapse)
|
||||||
when Rugged::Patch, Rugged::Diff::Delta
|
when Rugged::Patch, Rugged::Diff::Delta
|
||||||
init_from_rugged(raw_diff, collapse: collapse)
|
init_from_rugged(raw_diff, collapse: collapse)
|
||||||
|
when Gitaly::CommitDiffResponse
|
||||||
|
init_from_gitaly(raw_diff)
|
||||||
|
prune_diff_if_eligible(collapse)
|
||||||
when nil
|
when nil
|
||||||
raise "Nil as raw diff passed"
|
raise "Nil as raw diff passed"
|
||||||
else
|
else
|
||||||
|
@ -266,13 +270,26 @@ module Gitlab
|
||||||
@diff = encode!(strip_diff_headers(patch.to_s))
|
@diff = encode!(strip_diff_headers(patch.to_s))
|
||||||
end
|
end
|
||||||
|
|
||||||
def init_from_hash(hash, collapse: false)
|
def init_from_hash(hash)
|
||||||
raw_diff = hash.symbolize_keys
|
raw_diff = hash.symbolize_keys
|
||||||
|
|
||||||
serialize_keys.each do |key|
|
serialize_keys.each do |key|
|
||||||
send(:"#{key}=", raw_diff[key.to_sym])
|
send(:"#{key}=", raw_diff[key.to_sym])
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def init_from_gitaly(diff_msg)
|
||||||
|
@diff = diff_msg.raw_chunks.join
|
||||||
|
@new_path = encode!(diff_msg.to_path.dup)
|
||||||
|
@old_path = encode!(diff_msg.from_path.dup)
|
||||||
|
@a_mode = diff_msg.old_mode.to_s(8)
|
||||||
|
@b_mode = diff_msg.new_mode.to_s(8)
|
||||||
|
@new_file = diff_msg.from_id == BLANK_SHA
|
||||||
|
@renamed_file = diff_msg.from_path != diff_msg.to_path
|
||||||
|
@deleted_file = diff_msg.to_id == BLANK_SHA
|
||||||
|
end
|
||||||
|
|
||||||
|
def prune_diff_if_eligible(collapse = false)
|
||||||
prune_large_diff! if too_large?
|
prune_large_diff! if too_large?
|
||||||
prune_collapsed_diff! if collapse && collapsible?
|
prune_collapsed_diff! if collapse && collapsible?
|
||||||
end
|
end
|
||||||
|
|
|
@ -30,7 +30,9 @@ module Gitlab
|
||||||
elsif @deltas_only
|
elsif @deltas_only
|
||||||
each_delta(&block)
|
each_delta(&block)
|
||||||
else
|
else
|
||||||
each_patch(&block)
|
Gitlab::GitalyClient.migrate(:commit_raw_diffs) do
|
||||||
|
each_patch(&block)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -25,5 +25,19 @@ module Gitlab
|
||||||
def self.enabled?
|
def self.enabled?
|
||||||
gitaly_address.present?
|
gitaly_address.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.feature_enabled?(feature)
|
||||||
|
enabled? && ENV["GITALY_#{feature.upcase}"] == '1'
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.migrate(feature)
|
||||||
|
is_enabled = feature_enabled?(feature)
|
||||||
|
metric_name = feature.to_s
|
||||||
|
metric_name += "_gitaly" if is_enabled
|
||||||
|
|
||||||
|
Gitlab::Metrics.measure(metric_name) do
|
||||||
|
yield is_enabled
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
25
lib/gitlab/gitaly_client/commit.rb
Normal file
25
lib/gitlab/gitaly_client/commit.rb
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
module Gitlab
|
||||||
|
module GitalyClient
|
||||||
|
class Commit
|
||||||
|
# The ID of empty tree.
|
||||||
|
# See http://stackoverflow.com/a/40884093/1856239 and https://github.com/git/git/blob/3ad8b5bf26362ac67c9020bf8c30eee54a84f56d/cache.h#L1011-L1012
|
||||||
|
EMPTY_TREE_ID = '4b825dc642cb6eb9a060e54bf8d69288fbee4904'.freeze
|
||||||
|
|
||||||
|
class << self
|
||||||
|
def diff_from_parent(commit, options = {})
|
||||||
|
stub = Gitaly::Diff::Stub.new(nil, nil, channel_override: GitalyClient.channel)
|
||||||
|
repo = Gitaly::Repository.new(path: commit.project.repository.path_to_repo)
|
||||||
|
parent = commit.parents[0]
|
||||||
|
parent_id = parent ? parent.id : EMPTY_TREE_ID
|
||||||
|
request = Gitaly::CommitDiffRequest.new(
|
||||||
|
repository: repo,
|
||||||
|
left_commit_id: parent_id,
|
||||||
|
right_commit_id: commit.id
|
||||||
|
)
|
||||||
|
|
||||||
|
Gitlab::Git::DiffCollection.new(stub.commit_diff(request), options)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -109,6 +109,43 @@ EOT
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'using a Gitaly::CommitDiffResponse' do
|
||||||
|
let(:diff) do
|
||||||
|
described_class.new(
|
||||||
|
Gitaly::CommitDiffResponse.new(
|
||||||
|
to_path: ".gitmodules",
|
||||||
|
from_path: ".gitmodules",
|
||||||
|
old_mode: 0100644,
|
||||||
|
new_mode: 0100644,
|
||||||
|
from_id: '357406f3075a57708d0163752905cc1576fceacc',
|
||||||
|
to_id: '8e5177d718c561d36efde08bad36b43687ee6bf0',
|
||||||
|
raw_chunks: raw_chunks,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a small diff' do
|
||||||
|
let(:raw_chunks) { [@raw_diff_hash[:diff]] }
|
||||||
|
|
||||||
|
it 'initializes the diff' do
|
||||||
|
expect(diff.to_hash).to eq(@raw_diff_hash)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not prune the diff' do
|
||||||
|
expect(diff).not_to be_too_large
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'using a diff that is too large' do
|
||||||
|
let(:raw_chunks) { ['a' * 204800] }
|
||||||
|
|
||||||
|
it 'prunes the diff' do
|
||||||
|
expect(diff.diff).to be_empty
|
||||||
|
expect(diff).to be_too_large
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'straight diffs' do
|
describe 'straight diffs' do
|
||||||
|
|
58
spec/lib/gitlab/gitaly_client/commit_spec.rb
Normal file
58
spec/lib/gitlab/gitaly_client/commit_spec.rb
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Gitlab::GitalyClient::Commit do
|
||||||
|
describe '.diff_from_parent' do
|
||||||
|
let(:diff_stub) { double('Gitaly::Diff::Stub') }
|
||||||
|
let(:project) { create(:project, :repository) }
|
||||||
|
let(:repository_message) { Gitaly::Repository.new(path: project.repository.path) }
|
||||||
|
let(:commit) { project.commit('913c66a37b4a45b9769037c55c2d238bd0942d2e') }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(Gitaly::Diff::Stub).to receive(:new).and_return(diff_stub)
|
||||||
|
allow(diff_stub).to receive(:commit_diff).and_return([])
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a commit has a parent' do
|
||||||
|
it 'sends an RPC request with the parent ID as left commit' do
|
||||||
|
request = Gitaly::CommitDiffRequest.new(
|
||||||
|
repository: repository_message,
|
||||||
|
left_commit_id: 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660',
|
||||||
|
right_commit_id: commit.id,
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(diff_stub).to receive(:commit_diff).with(request)
|
||||||
|
|
||||||
|
described_class.diff_from_parent(commit)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a commit does not have a parent' do
|
||||||
|
it 'sends an RPC request with empty tree ref as left commit' do
|
||||||
|
initial_commit = project.commit('1a0b36b3cdad1d2ee32457c102a8c0b7056fa863')
|
||||||
|
request = Gitaly::CommitDiffRequest.new(
|
||||||
|
repository: repository_message,
|
||||||
|
left_commit_id: '4b825dc642cb6eb9a060e54bf8d69288fbee4904',
|
||||||
|
right_commit_id: initial_commit.id,
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(diff_stub).to receive(:commit_diff).with(request)
|
||||||
|
|
||||||
|
described_class.diff_from_parent(initial_commit)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a Gitlab::Git::DiffCollection' do
|
||||||
|
ret = described_class.diff_from_parent(commit)
|
||||||
|
|
||||||
|
expect(ret).to be_kind_of(Gitlab::Git::DiffCollection)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'passes options to Gitlab::Git::DiffCollection' do
|
||||||
|
options = { max_files: 31, max_lines: 13 }
|
||||||
|
|
||||||
|
expect(Gitlab::Git::DiffCollection).to receive(:new).with([], options)
|
||||||
|
|
||||||
|
described_class.diff_from_parent(commit, options)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -388,4 +388,32 @@ eos
|
||||||
expect(described_class.valid_hash?('a' * 41)).to be false
|
expect(described_class.valid_hash?('a' * 41)).to be false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#raw_diffs' do
|
||||||
|
context 'Gitaly commit_raw_diffs feature enabled' do
|
||||||
|
before do
|
||||||
|
allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(:commit_raw_diffs).and_return(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a truthy deltas_only is not passed to args' do
|
||||||
|
it 'fetches diffs from Gitaly server' do
|
||||||
|
expect(Gitlab::GitalyClient::Commit).to receive(:diff_from_parent).
|
||||||
|
with(commit)
|
||||||
|
|
||||||
|
commit.raw_diffs
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a truthy deltas_only is passed to args' do
|
||||||
|
it 'fetches diffs using Rugged' do
|
||||||
|
opts = { deltas_only: true }
|
||||||
|
|
||||||
|
expect(Gitlab::GitalyClient::Commit).not_to receive(:diff_from_parent)
|
||||||
|
expect(commit.raw).to receive(:diffs).with(opts)
|
||||||
|
|
||||||
|
commit.raw_diffs(opts)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue