From 921f0e567420b841be535b1e9acf20d8320cb2ea Mon Sep 17 00:00:00 2001 From: Max Howell Date: Tue, 6 Mar 2012 02:23:01 +0000 Subject: [PATCH] `brew update` will update taps --- Library/Homebrew/cmd/update.rb | 268 +++++++++++++-------------------- 1 file changed, 107 insertions(+), 161 deletions(-) diff --git a/Library/Homebrew/cmd/update.rb b/Library/Homebrew/cmd/update.rb index 7e4157a03c..3d77137844 100644 --- a/Library/Homebrew/cmd/update.rb +++ b/Library/Homebrew/cmd/update.rb @@ -1,192 +1,138 @@ module Homebrew extend self + def update abort "Please `brew install git' first." unless system "/usr/bin/which -s git" - updater = RefreshBrew.new - if updater.update_from_masterbrew! - updater.report - else - puts "Already up-to-date." - end - end -end - -class RefreshBrew - REPOSITORY_URL = "https://github.com/mxcl/homebrew.git" - FORMULA_DIR = 'Library/Formula/' - EXAMPLE_DIR = 'Library/Contributions/examples/' - - attr_reader :added_formulae, :updated_formulae, :deleted_formulae, :installed_formulae - attr_reader :added_examples, :deleted_examples - attr_reader :initial_revision, :current_revision - - def initialize - @added_formulae, @updated_formulae, @deleted_formulae, @installed_formulae = [], [], [], [] - @added_examples, @deleted_examples = [], [], [] - @initial_revision, @current_revision = nil - end - - # Performs an update of the homebrew source. Returns +true+ if a newer - # version was available, +false+ if already up-to-date. - def update_from_masterbrew! # ensure GIT_CONFIG is unset as we need to operate on .git/config ENV.delete('GIT_CONFIG') - HOMEBREW_REPOSITORY.cd do - if git_repo? - safe_system "git checkout -q master" - @initial_revision = read_revision - # originally we fetched by URL but then we decided that we should - # use origin so that it's easier for forks to operate seamlessly - unless `git remote`.split.include? 'origin' - safe_system "git remote add origin #{REPOSITORY_URL}" - end - else - begin - safe_system "git init" - safe_system "git config core.autocrlf false" - safe_system "git remote add origin #{REPOSITORY_URL}" - safe_system "git fetch origin" - safe_system "git reset --hard origin/master" - rescue Exception - safe_system "/bin/rm -rf .git" - raise + cd HOMEBREW_REPOSITORY + git_init_if_necessary + + report = Report.new + master_updater = Updater.new + master_updater.pull! + report.merge!(master_updater.report) + + Dir["Library/Taps/*"].each do |tapd| + cd tapd do + updater = Updater.new + updater.pull! + report.merge!(updater.report) do |key, oldval, newval| + oldval.concat(newval) end end - - # ensure we don't munge line endings on checkout - safe_system "git config core.autocrlf false" - # specify a refspec so that 'origin/master' gets updated - refspec = "refs/heads/master:refs/remotes/origin/master" - rebase = "--rebase" if ARGV.include? "--rebase" - execute "git pull #{rebase} origin #{refspec}" - @current_revision = read_revision end - if initial_revision && initial_revision != current_revision - # hash with status characters for keys: - # Added (A), Copied (C), Deleted (D), Modified (M), Renamed (R) - @changes_map = Hash.new {|h,k| h[k] = [] } - - changes = HOMEBREW_REPOSITORY.cd do - execute("git diff-tree -r --name-status -z #{initial_revision} #{current_revision}").split("\0") - end - - while status = changes.shift - file = changes.shift - @changes_map[status] << file - end - - if @changes_map.any? - @added_formulae = changed_items('A', FORMULA_DIR) - @deleted_formulae = changed_items('D', FORMULA_DIR) - @updated_formulae = changed_items('M', FORMULA_DIR) - @added_examples = changed_items('A', EXAMPLE_DIR) - @deleted_examples = changed_items('D', EXAMPLE_DIR) - @added_internal_commands = changed_items('A', "Library/Homebrew/cmd") - @deleted_internal_commands = changed_items('D', "Library/Homebrew/cmd") - - @installed_formulae = HOMEBREW_CELLAR.children. - select{ |pn| pn.directory? }. - map{ |pn| pn.basename.to_s }.sort if HOMEBREW_CELLAR.directory? - - return true - end - end - # assume nothing was updated - return false - end - - def git_repo? - Dir['.git/*'].length > 0 - end - - def pending_formulae_changes? - !@updated_formulae.empty? - end - - def pending_new_formulae? - !@added_formulae.empty? - end - - def deleted_formulae? - !@deleted_formulae.empty? - end - - def pending_examples_changes? - !@updated_examples.empty? - end - - def pending_new_examples? - !@added_examples.empty? - end - - def deleted_examples? - !@deleted_examples.empty? - end - - def report - puts "Updated Homebrew from #{initial_revision[0,8]} to #{current_revision[0,8]}." - if pending_new_formulae? - ohai "New formulae" - puts_columns added_formulae - end - if deleted_formulae? - ohai "Removed formulae" - puts_columns deleted_formulae, installed_formulae - end - if pending_formulae_changes? - ohai "Updated formulae" - puts_columns updated_formulae, installed_formulae - end - - unless @added_internal_commands.empty? - ohai "New commands" - puts_columns @added_internal_commands - end - unless @deleted_internal_commands.empty? - ohai "Removed commands" - puts_columns @deleted_internal_commands - end - - # external commands aren't generally documented but the distinction - # is loose. They are less "supported" and more "playful". - if pending_new_examples? - ohai "New external commands" - puts_columns added_examples - end - if deleted_examples? - ohai "Removed external commands" - puts_columns deleted_examples + if report.empty? + puts "Already up-to-date." + else + puts "Updated Homebrew from #{master_updater.initial_revision[0,8]} to #{master_updater.current_revision[0,8]}." + report.dump end end private - def read_revision - execute("git rev-parse HEAD").chomp + def git_init_if_necessary + if Dir['.git/*'].empty? + safe_system "git init" + safe_system "git config core.autocrlf false" + safe_system "git remote add origin https://github.com/mxcl/homebrew.git" + safe_system "git fetch origin" + safe_system "git reset --hard origin/master" + end + rescue Exception + FileUtils.rm_rf ".git" + raise end - def filter_by_directory(files, dir) - files.select { |f| f.index(dir) == 0 } +end + +class Updater + attr_reader :initial_revision, :current_revision + + def pull! + safe_system "git checkout -q master" + + @initial_revision = read_current_revision + + # ensure we don't munge line endings on checkout + safe_system "git config core.autocrlf false" + + args = ["pull"] + args << "--rebase" if ARGV.include? "--rebase" + args << "-q" unless ARGV.verbose? + args << "origin" + # the refspec ensures that 'origin/master' gets updated + args << "refs/heads/master:refs/remotes/origin/master" + + safe_system "git", *args + + @current_revision = read_current_revision end - def basenames(files) - files.map { |f| File.basename(f, '.rb') } + def report + map = Hash.new{ |h,k| h[k] = [] } + + if initial_revision && initial_revision != current_revision + changes = `git diff-tree -r --name-status -z #{initial_revision} #{current_revision}`.split("\0") + changes.each_slice(2) do |status, file| + file = Pathname.pwd.join(file).relative_path_from(HOMEBREW_REPOSITORY) + map[status.to_sym] << file.to_s + end + end + + map end - # extracts items by status from @changes_map - def changed_items(status, dir) - basenames(filter_by_directory(@changes_map[status], dir)).sort + private + + def read_current_revision + `git rev-parse HEAD`.chomp end - def execute(cmd) - out = `#{cmd}` + def `(cmd) + out = Kernel.`(cmd) #` if $? && !$?.success? $stderr.puts out - raise "Failed while executing #{cmd}" + raise ErrorDuringExecution, "Failure while executing: #{cmd}" end ohai(cmd, out) if ARGV.verbose? out end end + + +class Report < Hash + + def dump + # Key Legend: Added (A), Copied (C), Deleted (D), Modified (M), Renamed (R) + + dump_formula_report :A, "New Formula" + dump_formula_report :M, "Updated Formula" + dump_formula_report :D, "Deleted Formula" + dump_formula_report :R, "Renamed Formula" +# dump_new_commands +# dump_deleted_commands + end + + def select_formula key + fetch(key, []).map do |path| + case path when %r{^Library/Formula} + File.basename(path, ".rb") + when %r{^Library/Taps/(\w+)-(\w+)/(.*)} + "#$1/#$2/#{File.basename(path, '.rb')}" + end + end.compact.sort + end + + def dump_formula_report key, title + formula = select_formula(key) + unless formula.empty? + ohai title + puts_columns formula + end + end + +end