Skip to content

Instantly share code, notes, and snippets.

@awreece
Created August 10, 2018 23:25
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 awreece/a00a4bd5cc73b333dc67c07355b93d4b to your computer and use it in GitHub Desktop.
Save awreece/a00a4bd5cc73b333dc67c07355b93d4b to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
require 'digest'
module VanitySha
extend self
COMMITTER_PATTERN = /^committer .* \<.*\> (?<timestamp>\d+)(?<timezone>.*$)/
AUTHOR_PATTERN = /^author .* \<.*\> (?<timestamp>\d+)(?<timezone>.*$)/
TIMESTAMP_DELTA_MAX = 10 * 24 * 60 * 60
DELTAS = Enumerator.new do |enumerator|
for size in 1..2*TIMESTAMP_DELTA_MAX
for split in 0..size
enumerator << [split, size-split]
end
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 extract_author(commit_info)
author = commit_info
.split("\n")
.map {|line| line.match(AUTHOR_PATTERN)}
.compact
.first
[author.string, author[:timestamp].to_i, author[:timezone]]
end
def search(commit_info, hex_prefix)
committer_line, commit_ts = extract_committer(commit_info)
author_line, author_ts = extract_author(commit_info)
DELTAS.each do |commit_delta, author_delta|
new_committer_line = committer_line.sub(commit_ts.to_s, (commit_ts - commit_delta).to_s)
new_author_line = author_line.sub(author_ts.to_s, (author_ts - author_delta).to_s)
new_commit = commit_info.sub(committer_line, new_committer_line).sub(author_line, new_author_line)
new_sha = get_sha(new_commit)
puts new_sha, new_commit if new_sha.start_with?(hex_prefix)
return [commit_delta, author_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_commit_ts, original_commit_tz, new_author_ts, original_author_tz)
puts "LC_ALL=C GIT_COMMITTER_DATE=\"#{new_commit_ts}#{original_commit_tz}\" git commit --amend --date \"#{new_author_ts}#{original_author_tz}\" --no-edit"
`LC_ALL=C GIT_COMMITTER_DATE=\"#{new_commit_ts}#{original_commit_tz}\" git commit --amend --date \"#{new_author_ts}#{original_author_tz}\" --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_commit_ts, original_commit_tz = VanitySha.extract_committer(original_commit_info)
_, original_author_ts, original_author_tz = VanitySha.extract_author(original_commit_info)
puts "Searching for new SHA...\n"
result = VanitySha.search(original_commit_info, target_prefix)
if result
commit_delta, author_delta, sha = result
new_commit_ts = original_commit_ts - commit_delta
new_author_ts = original_author_ts - author_delta
puts "SHA found: #{sha.sub(target_prefix, "\e[32m#{target_prefix}\e[0m")}"
puts "Change committer timestamp to #{Time.at(new_commit_ts)}? This will amend your commit."
puts "Change author timestamp to #{Time.at(new_author_ts)}? This will amend your commit."
print "(y/n): "
if STDIN.gets.chomp == "y"
VanitySha.amend_commit(new_commit_ts, original_commit_tz, new_author_ts, original_author_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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment