bin/git-vanity-sha (view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
#!/usr/bin/env ruby
require 'digest'
module VanitySha
extend self
COMMITTER_PATTERN = /^committer .* \<.*\> (?<timestamp>\d+)(?<timezone>.*$)/
TIMESTAMP_DELTA_MAX = 10 * 24 * 60 * 60
DELTAS = Enumerator.new do |enumerator|
for i in 0..TIMESTAMP_DELTA_MAX
enumerator << i
enumerator << -i
end
end
def get_commit_info()
`git cat-file commit HEAD`
end
def extract_committer(commit_info)
committer = commit_info
.split("\n")
.map {|line| line.match(COMMITTER_PATTERN)}
.compact
.first
[committer.string, committer[:timestamp].to_i, committer[:timezone]]
end
def search(commit_info, hex_prefix)
committer_line, commit_ts = extract_committer(commit_info)
DELTAS.each do |delta|
new_committer_line = committer_line.sub(commit_ts.to_s, (commit_ts + delta).to_s)
new_commit = commit_info.sub(committer_line, new_committer_line)
new_sha = get_sha(new_commit)
return [delta, new_sha] if new_sha.start_with?(hex_prefix)
end
nil
end
def get_sha(commit_info)
Digest::SHA1.hexdigest "#{commit_header(commit_info)}#{commit_info}"
end
def amend_commit(new_ts, original_tz)
`LC_ALL=C GIT_COMMITTER_DATE=\"#{new_ts}#{original_tz}\" git commit --amend --no-edit`
end
def commit_header(commit_info)
"commit #{commit_info.length}\0"
end
end
if ARGV.length != 1 || ARGV[0].length < 1
puts "usage: git vanity-sha prefix"
puts "example: git vanity-sha CAFE"
exit(1)
end
target_prefix = ARGV[0].downcase
original_commit_info = VanitySha.get_commit_info()
_, original_ts, original_tz = VanitySha.extract_committer(original_commit_info)
puts "Searching for new SHA...\n"
result = VanitySha.search(original_commit_info, target_prefix)
if result
delta, sha = result
new_ts = original_ts + delta
puts "SHA found: #{sha.sub(target_prefix, "\e[32m#{target_prefix}\e[0m")}"
puts "Change committer timestamp to #{Time.at(new_ts)}? This will amend your commit."
print "(y/n): "
if STDIN.gets.chomp == "y"
VanitySha.amend_commit(new_ts, original_tz)
puts "\n" + "-" * 47
puts `git show --quiet --format=short HEAD`
else
puts "Aborting."
end
else
puts "Failed to generate a sha with prefix #{target_prefix}."
end
|