2017-01-04 13:43:06 -05:00
|
|
|
module Gitlab
|
|
|
|
module Git
|
|
|
|
class Blame
|
2017-06-01 17:21:14 -04:00
|
|
|
include Gitlab::EncodingHelper
|
2017-01-04 13:43:06 -05:00
|
|
|
|
|
|
|
attr_reader :lines, :blames
|
|
|
|
|
|
|
|
def initialize(repository, sha, path)
|
|
|
|
@repo = repository
|
|
|
|
@sha = sha
|
|
|
|
@path = path
|
|
|
|
@lines = []
|
|
|
|
@blames = load_blame
|
|
|
|
end
|
|
|
|
|
|
|
|
def each
|
|
|
|
@blames.each do |blame|
|
|
|
|
yield(
|
2017-07-25 16:48:17 -04:00
|
|
|
Gitlab::Git::Commit.new(@repo, blame.commit),
|
2017-01-04 13:43:06 -05:00
|
|
|
blame.line
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def load_blame
|
2017-07-28 08:16:26 -04:00
|
|
|
raw_output = @repo.gitaly_migrate(:blame) do |is_enabled|
|
|
|
|
if is_enabled
|
|
|
|
load_blame_by_gitaly
|
|
|
|
else
|
|
|
|
load_blame_by_shelling_out
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-01-04 13:43:06 -05:00
|
|
|
output = encode_utf8(raw_output)
|
|
|
|
process_raw_blame output
|
|
|
|
end
|
|
|
|
|
2017-07-28 08:16:26 -04:00
|
|
|
def load_blame_by_gitaly
|
|
|
|
@repo.gitaly_commit_client.raw_blame(@sha, @path)
|
|
|
|
end
|
|
|
|
|
|
|
|
def load_blame_by_shelling_out
|
|
|
|
cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{@repo.path} blame -p #{@sha} -- #{@path})
|
|
|
|
# Read in binary mode to ensure ASCII-8BIT
|
|
|
|
IO.popen(cmd, 'rb') {|io| io.read }
|
|
|
|
end
|
|
|
|
|
2017-01-04 13:43:06 -05:00
|
|
|
def process_raw_blame(output)
|
|
|
|
lines, final = [], []
|
|
|
|
info, commits = {}, {}
|
|
|
|
|
|
|
|
# process the output
|
|
|
|
output.split("\n").each do |line|
|
|
|
|
if line[0, 1] == "\t"
|
|
|
|
lines << line[1, line.size]
|
|
|
|
elsif m = /^(\w{40}) (\d+) (\d+)/.match(line)
|
|
|
|
commit_id, old_lineno, lineno = m[1], m[2].to_i, m[3].to_i
|
|
|
|
commits[commit_id] = nil unless commits.key?(commit_id)
|
|
|
|
info[lineno] = [commit_id, old_lineno]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# load all commits in single call
|
|
|
|
commits.keys.each do |key|
|
|
|
|
commits[key] = @repo.lookup(key)
|
|
|
|
end
|
|
|
|
|
|
|
|
# get it together
|
|
|
|
info.sort.each do |lineno, (commit_id, old_lineno)|
|
|
|
|
commit = commits[commit_id]
|
|
|
|
final << BlameLine.new(lineno, old_lineno, commit, lines[lineno - 1])
|
|
|
|
end
|
|
|
|
|
|
|
|
@lines = final
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class BlameLine
|
|
|
|
attr_accessor :lineno, :oldlineno, :commit, :line
|
|
|
|
def initialize(lineno, oldlineno, commit, line)
|
|
|
|
@lineno = lineno
|
|
|
|
@oldlineno = oldlineno
|
|
|
|
@commit = commit
|
|
|
|
@line = line
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|