Extract history introspection from "versions" command

This commit is contained in:
Jack Nagel 2014-05-28 16:23:33 -05:00
parent 17a27d3464
commit 56b041ca25
3 changed files with 98 additions and 105 deletions

View File

@ -2,7 +2,7 @@ require 'formula'
require 'bottles'
require 'tab'
require 'keg'
require 'cmd/versions'
require 'formula_versions'
require 'utils/inreplace'
require 'erb'
require 'extend/pathname'
@ -123,7 +123,9 @@ module Homebrew extend self
if ARGV.include? '--no-revision'
bottle_revision = 0
else
max = f.bottle_version_map('origin/master')[f.pkg_version].max
ohai "Determining #{f.name} bottle revision..."
versions = FormulaVersions.new(f)
max = versions.bottle_version_map("origin/master")[f.pkg_version].max
bottle_revision = max ? max + 1 : 0
end

View File

@ -1,10 +1,10 @@
require 'formula'
require "formula"
require "formula_versions"
module Homebrew extend self
def versions
raise "Please `brew install git` first" unless which "git"
raise "Please `brew update' first" unless (HOMEBREW_REPOSITORY/".git").directory?
raise "Please `brew update` first" unless (HOMEBREW_REPOSITORY/".git").directory?
raise FormulaUnspecifiedError if ARGV.named.empty?
opoo <<-EOS.undent
@ -13,107 +13,12 @@ module Homebrew extend self
https://github.com/Homebrew/homebrew-versions
EOS
ARGV.formulae.all? do |f|
relative_path = f.pretty_relative_path
f.versions do |version, sha|
print Tty.white.to_s
print "#{version.to_s.ljust(8)} "
print Tty.reset.to_s
puts "git checkout #{sha} #{relative_path}"
versions = FormulaVersions.new(f)
path = versions.repository_path
versions.each do |version, rev|
print "#{Tty.white}#{version.to_s.ljust(8)}#{Tty.reset} "
puts "git checkout #{rev} #{path}"
end
end
end
end
class Formula
def versions
versions = []
rev_list do |sha|
version = version_for_sha sha
unless versions.include? version or version.nil?
yield version, sha if block_given?
versions << version
end
end
return versions
end
def bottle_version_map branch='HEAD'
map = Hash.new { |h, k| h[k] = [] }
rev_list(branch) do |rev|
formula_for_sha(rev) do |f|
bottle = f.stable.bottle_specification
unless bottle.checksums.empty?
map[f.pkg_version] << bottle.revision
end
end
end
map
end
def pretty_relative_path
if Pathname.pwd == repository
entry_name
else
path
end
end
private
def repository
@repository ||= begin
if path.to_s =~ HOMEBREW_TAP_DIR_REGEX
HOMEBREW_REPOSITORY/"Library/Taps/#$1/#$2"
else
HOMEBREW_REPOSITORY
end
end
end
def entry_name
@entry_name ||= path.relative_path_from(repository).to_s
end
def rev_list branch='HEAD'
repository.cd do
IO.popen("git rev-list --abbrev-commit --remove-empty #{branch} -- #{entry_name}") do |io|
yield io.readline.chomp until io.eof?
end
end
end
def text_from_sha sha
repository.cd do
`git cat-file blob #{sha}:#{entry_name}`
end
end
IGNORED_EXCEPTIONS = [SyntaxError, TypeError, NameError,
ArgumentError, FormulaSpecificationError,
FormulaValidationError,]
def version_for_sha sha
formula_for_sha(sha) {|f| f.version }
end
def formula_for_sha sha, &block
mktemp do
path = Pathname.pwd.join("#{name}.rb")
path.write text_from_sha(sha)
# Unload the class so Formula#version returns the correct value
begin
old_const = Formulary.unload_formula name
nostdout { yield Formula.factory(path.to_s) }
rescue *IGNORED_EXCEPTIONS => e
# We rescue these so that we can skip bad versions and
# continue walking the history
ohai "#{e} in #{name} at revision #{sha}", e.backtrace if ARGV.debug?
rescue FormulaUnavailableError
# Suppress this error
ensure
Formulary.restore_formula name, old_const
end
end
end
end

View File

@ -0,0 +1,86 @@
class FormulaVersions
IGNORED_EXCEPTIONS = [
ArgumentError, NameError, SyntaxError, TypeError,
FormulaSpecificationError, FormulaValidationError,
]
attr_reader :f
def initialize(f)
@f = f
end
def repository
@repository ||= if f.path.to_s =~ HOMEBREW_TAP_DIR_REGEX
HOMEBREW_REPOSITORY/"Library/Taps/#$1/#$2"
else
HOMEBREW_REPOSITORY
end
end
def entry_name
@entry_name ||= f.path.relative_path_from(repository).to_s
end
def each
versions = Set.new
rev_list do |rev|
version = version_at_revision(rev)
next if version.nil?
yield version, rev if versions.add?(version)
end
end
def repository_path
Pathname.pwd == repository ? entry_name : f.path
end
def rev_list(branch="HEAD")
repository.cd do
IO.popen("git rev-list --abbrev-commit --remove-empty #{branch} -- #{entry_name}") do |io|
yield io.readline.chomp until io.eof?
end
end
end
def file_contents_at_revision(rev)
repository.cd { `git cat-file blob #{rev}:#{entry_name}` }
end
def version_at_revision(rev)
formula_at_revision(rev) { |f| f.version }
end
def formula_at_revision rev, &block
f.mktemp do
path = Pathname.pwd.join("#{f.name}.rb")
path.write file_contents_at_revision(rev)
begin
old_const = Formulary.unload_formula(f.name)
nostdout { yield Formula.factory(path.to_s) }
rescue *IGNORED_EXCEPTIONS => e
# We rescue these so that we can skip bad versions and
# continue walking the history
ohai "#{e} in #{f.name} at revision #{rev}", e.backtrace if ARGV.debug?
rescue FormulaUnavailableError
# Suppress this error
ensure
Formulary.restore_formula(f.name, old_const)
end
end
end
def bottle_version_map(branch="HEAD")
map = Hash.new { |h, k| h[k] = [] }
rev_list(branch) do |rev|
formula_at_revision(rev) do |f|
bottle = f.stable.bottle_specification
unless bottle.checksums.empty?
map[f.pkg_version] << bottle.revision
end
end
end
map
end
end