From 8c521ca3d0d1b1579f8f1ef3f76021217e485613 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Sun, 12 Jun 2011 17:07:59 +0200 Subject: [PATCH] refactor `update` command to use git diff instead of parsing `pull` output This fixes reporting of which formulae changed in git versions where `pull` output is not compatible. Signed-off-by: Adam Vandenberg --- Library/Homebrew/cmd/update.rb | 107 ++++++++++-------- .../test/fixtures/updater_fixture.yaml | 79 +++++-------- Library/Homebrew/test/test_updater.rb | 75 ++++++++---- 3 files changed, 138 insertions(+), 123 deletions(-) diff --git a/Library/Homebrew/cmd/update.rb b/Library/Homebrew/cmd/update.rb index b5492e9c10..944e7304a9 100644 --- a/Library/Homebrew/cmd/update.rb +++ b/Library/Homebrew/cmd/update.rb @@ -16,69 +16,71 @@ class RefreshBrew INIT_COMMAND = "git init" CHECKOUT_COMMAND = "git checkout -q master" UPDATE_COMMAND = "git pull #{REPOSITORY_URL} master" - REVISION_COMMAND = "git log -l -1 --pretty=format:%H 2> /dev/null" - GIT_UP_TO_DATE = "Already up-to-date." + REVISION_COMMAND = "git rev-parse HEAD" + DIFF_COMMAND = "git diff-tree -r --name-status -z %s %s" - formula_regexp = 'Library/Formula/(.+?)\.rb' - ADDED_FORMULA = %r{^\s+create mode \d+ #{formula_regexp}$} - UPDATED_FORMULA = %r{^\s+#{formula_regexp}\s} - DELETED_FORMULA = %r{^\s+delete mode \d+ #{formula_regexp}$} - - example_regexp = 'Library/Contributions/examples/([^.\s]+).*' - ADDED_EXAMPLE = %r{^\s+create mode \d+ #{example_regexp}$} - UPDATED_EXAMPLE = %r{^\s+#{example_regexp}} - DELETED_EXAMPLE = %r{^\s+delete mode \d+ #{example_regexp}$} + FORMULA_DIR = 'Library/Formula/' + EXAMPLE_DIR = 'Library/Contributions/examples/' attr_reader :added_formulae, :updated_formulae, :deleted_formulae, :installed_formulae attr_reader :added_examples, :updated_examples, :deleted_examples - attr_reader :initial_revision + attr_reader :initial_revision, :current_revision def initialize @added_formulae, @updated_formulae, @deleted_formulae, @installed_formulae = [], [], [], [] @added_examples, @updated_examples, @deleted_examples = [], [], [] - @initial_revision = self.current_revision + @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! - output = '' HOMEBREW_REPOSITORY.cd do - if File.directory? '.git' + if git_repo? safe_system CHECKOUT_COMMAND + @initial_revision = read_revision else safe_system INIT_COMMAND end - output = execute(UPDATE_COMMAND) + execute(UPDATE_COMMAND) + @current_revision = read_revision end - output.split("\n").reverse.each do |line| - case line - when ADDED_FORMULA - @added_formulae << $1 - when DELETED_FORMULA - @deleted_formulae << $1 - when UPDATED_FORMULA - @updated_formulae << $1 unless @added_formulae.include?($1) or @deleted_formulae.include?($1) - when ADDED_EXAMPLE - @added_examples << $1 - when DELETED_EXAMPLE - @deleted_examples << $1 - when UPDATED_EXAMPLE - @updated_examples << $1 unless @added_examples.include?($1) or @deleted_examples.include?($1) + 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(DIFF_COMMAND % [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) + @updated_examples = changed_items('M', EXAMPLE_DIR) + + @installed_formulae = HOMEBREW_CELLAR.children. + select{ |pn| pn.directory? }. + map{ |pn| pn.basename.to_s }.sort + + return true end end - @added_formulae.sort! - @updated_formulae.sort! - @deleted_formulae.sort! - @added_examples.sort! - @updated_examples.sort! - @deleted_examples.sort! - @installed_formulae = HOMEBREW_CELLAR.children. - select{ |pn| pn.directory? }. - map{ |pn| pn.basename.to_s }.sort + # assume nothing was updated + return false + end - output.strip != GIT_UP_TO_DATE + def git_repo? + File.directory? '.git' end def pending_formulae_changes? @@ -105,12 +107,6 @@ class RefreshBrew !@deleted_examples.empty? end - def current_revision - HOMEBREW_REPOSITORY.cd { execute(REVISION_COMMAND).strip } - rescue - 'TAIL' - end - def report puts "Updated Homebrew from #{initial_revision[0,8]} to #{current_revision[0,8]}." ## New Formulae @@ -151,10 +147,27 @@ class RefreshBrew private + def read_revision + execute(REVISION_COMMAND).chomp + end + + def filter_by_directory(files, dir) + files.select { |f| f.index(dir) == 0 } + end + + def basenames(files) + files.map { |f| File.basename(f, '.rb') } + end + + # extracts items by status from @changes_map + def changed_items(status, dir) + basenames(filter_by_directory(@changes_map[status], dir)).sort + end + def execute(cmd) out = `#{cmd}` if $? && !$?.success? - puts out + $stderr.puts out raise "Failed while executing #{cmd}" end ohai(cmd, out) if ARGV.verbose? diff --git a/Library/Homebrew/test/fixtures/updater_fixture.yaml b/Library/Homebrew/test/fixtures/updater_fixture.yaml index 55e9d0f407..b3dd14d230 100644 --- a/Library/Homebrew/test/fixtures/updater_fixture.yaml +++ b/Library/Homebrew/test/fixtures/updater_fixture.yaml @@ -1,53 +1,26 @@ -update_git_pull_output_without_formulae_changes: | - remote: counting objects: 58, done. - remote: Compressing objects: 100% (35/35), done. - remote: Total 39 (delta 20), reused 0 (delta 0) - Unpacking objects: 100% (39/39), done. - From git://github.com/mxcl/homebrew - * branch master -> FETCH_HEAD - Updating 14ef7f9..f414bc8 - Fast forward - Library/Homebrew/ARGV+yeast.rb | 35 ++-- - Library/Homebrew/beer_events.rb | 181 +++++++++++++ - Library/Homebrew/hardware.rb | 71 ++++++ - Library/Homebrew/hw.model.c | 17 -- - README | 337 +++++++++++++------------ - bin/brew | 137 ++++++++--- - 40 files changed, 1107 insertions(+), 426 deletions(-) - create mode 100644 Library/Homebrew/beer_events.rb - create mode 100644 Library/Homebrew/hardware.rb - delete mode 100644 Library/Homebrew/hw.model.c - delete mode 100644 Library/Homebrew/hw.model.rb -update_git_pull_output_with_formulae_changes: | - remote: counting objects: 58, done. - remote: Compressing objects: 100% (35/35), done. - remote: Total 39 (delta 20), reused 0 (delta 0) - Unpacking objects: 100% (39/39), done. - From git://github.com/mxcl/homebrew - * branch master -> FETCH_HEAD - Updating 14ef7f9..f414bc8 - Fast forward - Library/Contributions/brew_bash_completion.sh | 6 +- - Library/Formula/antiword.rb | 13 + - Library/Formula/bash-completion.rb | 25 ++ - Library/Formula/xar.rb | 19 ++ - Library/Formula/yajl.rb | 2 +- - Library/Homebrew/ARGV+yeast.rb | 35 ++-- - Library/Homebrew/beer_events.rb | 181 +++++++++++++ - Library/Homebrew/hardware.rb | 71 ++++++ - Library/Homebrew/hw.model.c | 17 -- - Library/Homebrew/pathname+yeast.rb | 28 ++- - Library/Homebrew/unittest.rb | 106 ++++++++- - Library/Homebrew/utils.rb | 36 ++- - README | 337 +++++++++++++------------ - bin/brew | 137 ++++++++--- - 40 files changed, 1107 insertions(+), 426 deletions(-) - create mode 100644 Library/Formula/antiword.rb - create mode 100644 Library/Formula/bash-completion.rb - create mode 100644 Library/Formula/ddrescue.rb - create mode 100644 Library/Formula/dict.rb - create mode 100644 Library/Formula/lua.rb - create mode 100644 Library/Homebrew/beer_events.rb - create mode 100644 Library/Homebrew/hardware.rb - delete mode 100644 Library/Homebrew/hw.model.c - delete mode 100644 Library/Homebrew/hw.model.rb +update_git_diff_output_without_formulae_changes: | + M Library/Homebrew/ARGV+yeast.rb + A Library/Homebrew/beer_events.rb + A Library/Homebrew/hardware.rb + D Library/Homebrew/hw.model.c + M README + M bin/brew +update_git_diff_output_with_formulae_changes: | + M Library/Contributions/brew_bash_completion.sh + A Library/Formula/antiword.rb + A Library/Formula/bash-completion.rb + M Library/Formula/xar.rb + M Library/Formula/yajl.rb + M Library/Homebrew/ARGV+yeast.rb + M Library/Homebrew/pathname+yeast.rb + M Library/Homebrew/unittest.rb + M Library/Homebrew/utils.rb + M README + M bin/brew + A Library/Formula/ddrescue.rb + A Library/Formula/dict.rb + A Library/Formula/lua.rb + A Library/Homebrew/beer_events.rb + A Library/Homebrew/hardware.rb + D Library/Homebrew/hw.model.c + D Library/Homebrew/hw.model.rb diff --git a/Library/Homebrew/test/test_updater.rb b/Library/Homebrew/test/test_updater.rb index 348658d250..b5247f6840 100644 --- a/Library/Homebrew/test/test_updater.rb +++ b/Library/Homebrew/test/test_updater.rb @@ -11,15 +11,23 @@ require 'utils' require 'cmd/update' class RefreshBrewMock < RefreshBrew - def in_prefix_expect(expect, returns = '') - @expect ||= {} - @expect[expect] = returns + def git_repo? + @git_repo + end + attr_writer :git_repo + + def in_prefix_expect(cmd, output = '') + @outputs ||= Hash.new { |h,k| h[k] = [] } + @expected ||= [] + @expected << cmd + @outputs[cmd] << output end def `(cmd) - if Dir.pwd == HOMEBREW_PREFIX.to_s and @expect.has_key?(cmd) - (@called ||= []) << cmd - @expect[cmd] + if Dir.pwd == HOMEBREW_PREFIX.to_s and @expected.include?(cmd) and !@outputs[cmd].empty? + @called ||= [] + @called << cmd + @outputs[cmd].shift else raise "#{inspect} Unexpectedly called backticks in pwd `#{HOMEBREW_PREFIX}' and command `#{cmd}'" end @@ -28,7 +36,7 @@ class RefreshBrewMock < RefreshBrew alias safe_system ` def expectations_met? - @expect.keys.sort == @called.sort + @expected == @called end def inspect @@ -54,11 +62,30 @@ class UpdaterTests < Test::Unit::TestCase @fixture_data end + def test_init_homebrew + outside_prefix do + updater = RefreshBrewMock.new + updater.git_repo = false + updater.in_prefix_expect("git init") + updater.in_prefix_expect("git pull #{RefreshBrewMock::REPOSITORY_URL} master") + updater.in_prefix_expect("git rev-parse HEAD", "1234abcd") + + assert_equal false, updater.update_from_masterbrew! + assert updater.expectations_met? + assert updater.updated_formulae.empty? + assert updater.added_formulae.empty? + end + end + def test_update_homebrew_without_any_changes outside_prefix do updater = RefreshBrewMock.new - updater.in_prefix_expect(RefreshBrew::INIT_COMMAND) - updater.in_prefix_expect(RefreshBrew::UPDATE_COMMAND, "Already up-to-date.\n") + updater.git_repo = true + updater.in_prefix_expect("git checkout -q master") + updater.in_prefix_expect("git rev-parse HEAD", "1234abcd") + updater.in_prefix_expect("git pull #{RefreshBrewMock::REPOSITORY_URL} master") + updater.in_prefix_expect("git rev-parse HEAD", "3456cdef") + updater.in_prefix_expect("git diff-tree -r --name-status -z 1234abcd 3456cdef", "") assert_equal false, updater.update_from_masterbrew! assert updater.expectations_met? @@ -70,9 +97,14 @@ class UpdaterTests < Test::Unit::TestCase def test_update_homebrew_without_formulae_changes outside_prefix do updater = RefreshBrewMock.new - updater.in_prefix_expect(RefreshBrew::INIT_COMMAND) - output = fixture('update_git_pull_output_without_formulae_changes') - updater.in_prefix_expect(RefreshBrew::UPDATE_COMMAND, output) + updater.git_repo = true + diff_output = fixture('update_git_diff_output_without_formulae_changes') + + updater.in_prefix_expect("git checkout -q master") + updater.in_prefix_expect("git rev-parse HEAD", "1234abcd") + updater.in_prefix_expect("git pull #{RefreshBrewMock::REPOSITORY_URL} master") + updater.in_prefix_expect("git rev-parse HEAD", "3456cdef") + updater.in_prefix_expect("git diff-tree -r --name-status -z 1234abcd 3456cdef", diff_output.gsub(/\s+/, "\0")) assert_equal true, updater.update_from_masterbrew! assert !updater.pending_formulae_changes? @@ -84,9 +116,14 @@ class UpdaterTests < Test::Unit::TestCase def test_update_homebrew_with_formulae_changes outside_prefix do updater = RefreshBrewMock.new - updater.in_prefix_expect(RefreshBrew::INIT_COMMAND) - output = fixture('update_git_pull_output_with_formulae_changes') - updater.in_prefix_expect(RefreshBrew::UPDATE_COMMAND, output) + updater.git_repo = true + diff_output = fixture('update_git_diff_output_with_formulae_changes') + + updater.in_prefix_expect("git checkout -q master") + updater.in_prefix_expect("git rev-parse HEAD", "1234abcd") + updater.in_prefix_expect("git pull #{RefreshBrewMock::REPOSITORY_URL} master") + updater.in_prefix_expect("git rev-parse HEAD", "3456cdef") + updater.in_prefix_expect("git diff-tree -r --name-status -z 1234abcd 3456cdef", diff_output.gsub(/\s+/, "\0")) assert_equal true, updater.update_from_masterbrew! assert updater.pending_formulae_changes? @@ -94,12 +131,4 @@ class UpdaterTests < Test::Unit::TestCase assert_equal %w{ antiword bash-completion ddrescue dict lua }, updater.added_formulae end end - - def test_updater_returns_current_revision - outside_prefix do - updater = RefreshBrewMock.new - updater.in_prefix_expect(RefreshBrew::REVISION_COMMAND, 'the-revision-hash') - assert_equal 'the-revision-hash', updater.current_revision - end - end end