summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouwe Maan <douwe@selenight.nl>2017-09-22 11:30:37 +0200
committerDouwe Maan <douwe@selenight.nl>2017-09-25 10:22:28 +0200
commitb9857d8b662a2dbbf54f46ecdcecb44702affe55 (patch)
tree8a25798bc0c8e1c15ae414ac667ff5465f6fefa2
parentc19d5ac1a2ac75fd7200aacae3514e5600a7f648 (diff)
downloadgitlab-ce-b9857d8b662a2dbbf54f46ecdcecb44702affe55.tar.gz
Properly compare diff refs and diff positions when shas are truncated
-rw-r--r--app/models/commit.rb11
-rw-r--r--lib/gitlab/diff/diff_refs.rb22
-rw-r--r--lib/gitlab/diff/position.rb11
3 files changed, 31 insertions, 13 deletions
diff --git a/app/models/commit.rb b/app/models/commit.rb
index 2ae8890c1b3..c85b5b93607 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -26,7 +26,8 @@ class Commit
DIFF_HARD_LIMIT_LINES = 50000
# The SHA can be between 7 and 40 hex characters.
- COMMIT_SHA_PATTERN = '\h{7,40}'.freeze
+ MIN_SHA_LENGTH = 7
+ COMMIT_SHA_PATTERN = /\h{#{MIN_SHA_LENGTH},40}/.freeze
def banzai_render_context(field)
context = { pipeline: :single_line, project: self.project }
@@ -53,7 +54,7 @@ class Commit
# Truncate sha to 8 characters
def truncate_sha(sha)
- sha[0..7]
+ sha[0..MIN_SHA_LENGTH]
end
def max_diff_options
@@ -100,7 +101,7 @@ class Commit
def self.reference_pattern
@reference_pattern ||= %r{
(?:#{Project.reference_pattern}#{reference_prefix})?
- (?<commit>\h{7,40})
+ (?<commit>#{COMMIT_SHA_PATTERN})
}x
end
@@ -216,9 +217,9 @@ class Commit
@raw.respond_to?(method, include_private) || super
end
- # Truncate sha to 8 characters
+ # Truncate sha to 7 characters
def short_id
- @raw.short_id(7)
+ @raw.short_id(MIN_SHA_LENGTH)
end
def diff_refs
diff --git a/lib/gitlab/diff/diff_refs.rb b/lib/gitlab/diff/diff_refs.rb
index 371cbe04b9b..1c4c1deb7a9 100644
--- a/lib/gitlab/diff/diff_refs.rb
+++ b/lib/gitlab/diff/diff_refs.rb
@@ -13,9 +13,9 @@ module Gitlab
def ==(other)
other.is_a?(self.class) &&
- base_sha == other.base_sha &&
- start_sha == other.start_sha &&
- head_sha == other.head_sha
+ shas_equal?(base_sha, other.base_sha) &&
+ shas_equal?(start_sha, other.start_sha) &&
+ shas_equal?(head_sha, other.head_sha)
end
alias_method :eql?, :==
@@ -47,6 +47,22 @@ module Gitlab
CompareService.new(project, head_sha).execute(project, start_sha, straight: straight)
end
end
+
+ private
+
+ def shas_equal?(sha1, sha2)
+ return true if sha1 == sha2
+ return false if sha1.nil? || sha2.nil?
+ return false unless sha1.class == sha2.class
+
+ length = [sha1.length, sha2.length].min
+
+ # If either of the shas is below 7 characters in length, we cannot be sure
+ # if they actually refer to the same commit because of hash collision.
+ return false if length < Commit::MIN_SHA_LENGTH
+
+ sha1[0, length] == sha2[0, length]
+ end
end
end
end
diff --git a/lib/gitlab/diff/position.rb b/lib/gitlab/diff/position.rb
index f80afb20f0c..b8db3adef0a 100644
--- a/lib/gitlab/diff/position.rb
+++ b/lib/gitlab/diff/position.rb
@@ -49,12 +49,13 @@ module Gitlab
coder['attributes'] = self.to_h
end
- def key
- @key ||= [base_sha, start_sha, head_sha, Digest::SHA1.hexdigest(old_path || ""), Digest::SHA1.hexdigest(new_path || ""), old_line, new_line]
- end
-
def ==(other)
- other.is_a?(self.class) && key == other.key
+ other.is_a?(self.class) &&
+ other.diff_refs == diff_refs &&
+ other.old_path == old_path &&
+ other.new_path == new_path &&
+ other.old_line == old_line &&
+ other.new_line == new_line
end
def to_h