diff --git a/Library/Homebrew/cmd/bottle.rb b/Library/Homebrew/cmd/bottle.rb index e9e39d49cb..6d3f9ba92d 100644 --- a/Library/Homebrew/cmd/bottle.rb +++ b/Library/Homebrew/cmd/bottle.rb @@ -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 diff --git a/Library/Homebrew/cmd/versions.rb b/Library/Homebrew/cmd/versions.rb index af59ccb3a0..302c7c3bce 100644 --- a/Library/Homebrew/cmd/versions.rb +++ b/Library/Homebrew/cmd/versions.rb @@ -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 diff --git a/Library/Homebrew/formula_versions.rb b/Library/Homebrew/formula_versions.rb new file mode 100644 index 0000000000..d4bad45844 --- /dev/null +++ b/Library/Homebrew/formula_versions.rb @@ -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