Skip to content

Instantly share code, notes, and snippets.

@jcoglan
Last active February 13, 2019 15:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jcoglan/2648ca7db64f5e94ba76059915028dc7 to your computer and use it in GitHub Desktop.
Save jcoglan/2648ca7db64f5e94ba76059915028dc7 to your computer and use it in GitHub Desktop.
require_relative "./base"
require_relative "../diff"
require_relative "../rev_list"
module Command
class Blame < Base
def run
@lines = []
@commits = []
@chain = nil
revs = ::RevList.new(repo, [@args.first])
revs.each do |commit|
tree_diff = revs.tree_diff(commit.parent, commit.oid)
index_commit(commit, tree_diff.values.first)
@commits << commit
end
@author_width = @commits.map { |c| c.author.name.size }.max
@number_width = @lines.size.to_s.size
setup_pager
@lines.each_with_index do |(commit, line), i|
print_line(commit, i + 1, line)
end
exit 0
end
private
def index_commit(commit, entries)
a, b = entries.map { |entry| read_blob(entry) }
@next_chain = {}
::Diff.diff(a, b).each { |edit| index_edit(commit, edit) }
@chain = @next_chain
end
def read_blob(entry)
entry ? repo.database.load(entry.oid).data : ""
end
def index_edit(commit, edit)
b_number = edit.b_line&.number
number = @chain ? @chain[b_number] : b_number
return unless number
case edit.type
when :eql
@next_chain[edit.a_line.number] = number
when :ins
@lines[number - 1] = [commit, edit.b_line.text]
end
end
def print_line(commit, number, line)
puts format("%s (%-#{ @author_width }s %s %#{ @number_width }d) %s",
commit.oid[0..7],
commit.author.name,
commit.author.time.strftime("%Y-%m-%d %H:%M:%S %z"),
number,
line)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment