Skip to content
Snippets Groups Projects
Commit 528b5eeb authored by Sean McGivern's avatar Sean McGivern
Browse files

Fix error when viewing diffs without blobs

Old merge requests can have diffs without corresponding blobs. (This also may be
possible for commit diffs in corrupt repositories.)

We can't use the `&.` operator on the blobs, because the blob objects are never
nil, but `BatchLoader` instances that delegate to `Blob`. We can't use
`Object#try`, because `Blob` doesn't inherit from `Object`.

`BatchLoader` provides a `__sync` method that returns the delegated object, but
using `itself` also works because it's forwarded, and will work for
non-`BatchLoader` instances too. So the simplest solution is to just use that
with the `&.` operator.
parent e5a9b9a1
No related branches found
No related tags found
No related merge requests found
---
title: Fix viewing merge request diffs where the underlying blobs are unavailable
merge_request:
author:
type: fixed
Loading
Loading
@@ -116,8 +116,10 @@ module Gitlab
new_content_sha || old_content_sha
end
 
# Use #itself to check the value wrapped by a BatchLoader instance, rather
# than if the BatchLoader instance itself is falsey.
def blob
new_blob || old_blob
new_blob&.itself || old_blob&.itself
end
 
attr_writer :highlighted_diff_lines
Loading
Loading
@@ -173,7 +175,7 @@ module Gitlab
end
 
def binary?
has_binary_notice? || old_blob&.binary? || new_blob&.binary?
has_binary_notice? || try_blobs(:binary?)
end
 
def text?
Loading
Loading
@@ -181,15 +183,15 @@ module Gitlab
end
 
def external_storage_error?
old_blob&.external_storage_error? || new_blob&.external_storage_error?
try_blobs(:external_storage_error?)
end
 
def stored_externally?
old_blob&.stored_externally? || new_blob&.stored_externally?
try_blobs(:stored_externally?)
end
 
def external_storage
old_blob&.external_storage || new_blob&.external_storage
try_blobs(:external_storage)
end
 
def content_changed?
Loading
Loading
@@ -204,15 +206,15 @@ module Gitlab
end
 
def size
[old_blob&.size, new_blob&.size].compact.sum
valid_blobs.map(&:size).sum
end
 
def raw_size
[old_blob&.raw_size, new_blob&.raw_size].compact.sum
valid_blobs.map(&:raw_size).sum
end
 
def raw_binary?
old_blob&.raw_binary? || new_blob&.raw_binary?
try_blobs(:raw_binary?)
end
 
def raw_text?
Loading
Loading
@@ -235,6 +237,19 @@ module Gitlab
 
private
 
# The blob instances are instances of BatchLoader, which means calling
# &. directly on them won't work. Object#try also won't work, because Blob
# doesn't inherit from Object, but from BasicObject (via SimpleDelegator).
def try_blobs(meth)
old_blob&.itself&.public_send(meth) || new_blob&.itself&.public_send(meth)
end
# We can't use #compact for the same reason we can't use &., but calling
# #nil? explicitly does work because it is proxied to the blob itself.
def valid_blobs
[old_blob, new_blob].reject(&:nil?)
end
def text_position_properties(line)
{ old_line: line.old_line, new_line: line.new_line }
end
Loading
Loading
Loading
Loading
@@ -431,4 +431,29 @@ describe Gitlab::Diff::File do
end
end
end
context 'when neither blob exists' do
let(:blank_diff_refs) { Gitlab::Diff::DiffRefs.new(base_sha: Gitlab::Git::BLANK_SHA, head_sha: Gitlab::Git::BLANK_SHA) }
let(:diff_file) { described_class.new(diff, diff_refs: blank_diff_refs, repository: project.repository) }
describe '#blob' do
it 'returns a concrete nil so it can be used in boolean expressions' do
actual = diff_file.blob && true
expect(actual).to be_nil
end
end
describe '#binary?' do
it 'returns false' do
expect(diff_file).not_to be_binary
end
end
describe '#size' do
it 'returns zero' do
expect(diff_file.size).to be_zero
end
end
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment