Created
March 2, 2015 19:01
-
-
Save aalin/6a69ac4ffda4645482d5 to your computer and use it in GitHub Desktop.
Git blame with different colors depending when each line was changed. Darker = older, lighter = newer.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'shellwords' | |
class Blamify | |
PORCELAIN_COMMIT_HASH_RE = /^(?<hash>\h{40})\s/ | |
PORCELAIN_LINE_RE = /^\t(?<line>.*)$/ | |
COLORS = (237..255).to_a # Greyscale gradient | |
def initialize(filename) | |
@filename = filename | |
end | |
def print(output = $stdout) | |
committer_times = blames.map { |blame| blame[:committer_time].to_i } | |
min_time = committer_times.min | |
max_time = committer_times.max | |
timespan = max_time - min_time | |
mid_time = min_time + timespan / 2 | |
blames.each do |blame| | |
committer_time = blame[:committer_time].to_i | |
offset = committer_time - min_time | |
Time.at(committer_time) | |
color = color_at(offset / timespan.to_f) | |
author = blame.fetch(:author, "") | |
output.puts format("\e[38;5;%dm%-14s: %s\e[0m", color, author[0, 14], blame[:line]) | |
end | |
end | |
private | |
def color_at(x) | |
x = 0 if x.nan? | |
x = [[x, 0.0].max, 1.0].min # Clamp to (0.0, 1.0) | |
COLORS[(x * COLORS.size.pred).floor] | |
end | |
def blames | |
@blames ||= load_blames | |
end | |
def load_blames | |
blame_data = {} | |
load_porcelain.lines.each_with_object([]) do |line, blames| | |
case line | |
when PORCELAIN_COMMIT_HASH_RE | |
blame_data[:commit] = $~[:hash] | |
when PORCELAIN_LINE_RE | |
blames << blame_data.merge(line: $~[:line]) | |
else # Key/value | |
key, value = line.chomp.split(' ', 2) | |
key.tr!('-', '_') | |
blame_data[key.to_sym] = value | |
end | |
end | |
end | |
def load_porcelain | |
`#{ Shellwords.shelljoin(['git', 'blame', '-p', @filename]) }` | |
end | |
end | |
if file = ARGV.first | |
dir = File.dirname(File.expand_path(file)) | |
Dir.chdir(dir) do | |
Blamify.new(File.basename(file)).print | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment