From f59eb358c29c5f40601a99e3f1bf7e8e891f10ba Mon Sep 17 00:00:00 2001
From: Mike McQuaid <mike@mikemcquaid.com>
Date: Mon, 20 Mar 2017 20:37:12 +0100
Subject: [PATCH] missing_formula: subsume historic logic.

These methods belong together so combine them in a single class to
provide a simpler API.
---
 Library/Homebrew/cmd/info.rb                  | 17 +-----
 Library/Homebrew/cmd/install.rb               | 16 +----
 Library/Homebrew/cmd/search.rb                |  4 +-
 Library/Homebrew/exceptions.rb                | 13 -----
 Library/Homebrew/historic.rb                  | 57 ------------------
 Library/Homebrew/missing_formula.rb           | 55 +++++++++++++++++-
 Library/Homebrew/test/historic_test.rb        | 46 ---------------
 Library/Homebrew/test/missing_formula_spec.rb | 58 ++++++++++++++++++-
 8 files changed, 116 insertions(+), 150 deletions(-)
 delete mode 100644 Library/Homebrew/historic.rb
 delete mode 100644 Library/Homebrew/test/historic_test.rb

diff --git a/Library/Homebrew/cmd/info.rb b/Library/Homebrew/cmd/info.rb
index 5c96e5c506..7e18155567 100644
--- a/Library/Homebrew/cmd/info.rb
+++ b/Library/Homebrew/cmd/info.rb
@@ -23,7 +23,6 @@ require "formula"
 require "keg"
 require "tab"
 require "json"
-require "historic"
 
 module Homebrew
   module_function
@@ -57,22 +56,10 @@ module Homebrew
           end
         rescue FormulaUnavailableError => e
           # No formula with this name, try a missing formula lookup
-          if (missing_formula = Homebrew::MissingFormula.missing_formula(f))
-            ofail "#{e.message}\n#{missing_formula}"
+          if (reason = Homebrew::MissingFormula.reason(f))
+            ofail "#{e.message}\n#{reason}"
           else
             ofail e.message
-
-            # No point in searching if the specified tap isn't tapped yet
-            next if e.instance_of?(TapFormulaUnavailableError) && !e.tap.installed?
-
-            migrations = search_for_migrated_formula(f)
-            next unless migrations.empty?
-            ohai "Searching among deleted formulae..."
-            begin
-              search_for_deleted_formula(f)
-            rescue
-              nil
-            end
           end
         end
       end
diff --git a/Library/Homebrew/cmd/install.rb b/Library/Homebrew/cmd/install.rb
index a0f95887b1..bd7897171f 100644
--- a/Library/Homebrew/cmd/install.rb
+++ b/Library/Homebrew/cmd/install.rb
@@ -62,7 +62,6 @@ require "formula_installer"
 require "tap"
 require "hardware"
 require "development_tools"
-require "historic"
 
 module Homebrew
   module_function
@@ -207,24 +206,13 @@ module Homebrew
       # formula was found, but there's a problem with its implementation).
       ofail e.message
     rescue FormulaUnavailableError => e
-      if (missing_formula = Homebrew::MissingFormula.missing_formula(e.name))
-        ofail "#{e.message}\n#{missing_formula}"
+      if (reason = Homebrew::MissingFormula.reason(e.name))
+        ofail "#{e.message}\n#{reason}"
       elsif e.name == "updog"
         ofail "What's updog?"
       else
         ofail e.message
 
-        migrations = search_for_migrated_formula(e.name)
-        return unless migrations.empty?
-
-        ohai "Searching among deleted formulae..."
-        begin
-          search_for_deleted_formula(e.name)
-          return
-        rescue
-          nil
-        end
-
         query = query_regexp(e.name)
 
         ohai "Searching for similarly named formulae..."
diff --git a/Library/Homebrew/cmd/search.rb b/Library/Homebrew/cmd/search.rb
index 6887805d6e..db5898872f 100644
--- a/Library/Homebrew/cmd/search.rb
+++ b/Library/Homebrew/cmd/search.rb
@@ -67,12 +67,12 @@ module Homebrew
       if $stdout.tty?
         count = local_results.length + tap_results.length
 
-        if msg = Homebrew::MissingFormula.missing_formula(query)
+        if reason = Homebrew::MissingFormula.reason(query)
           if count > 0
             puts
             puts "If you meant #{query.inspect} specifically:"
           end
-          puts msg
+          puts reason
         elsif count.zero?
           puts "No formula found for #{query.inspect}."
           begin
diff --git a/Library/Homebrew/exceptions.rb b/Library/Homebrew/exceptions.rb
index e9885890bb..77da4489e8 100644
--- a/Library/Homebrew/exceptions.rb
+++ b/Library/Homebrew/exceptions.rb
@@ -94,19 +94,6 @@ class TapFormulaUnavailableError < FormulaUnavailableError
   end
 end
 
-class FormulaExistsError < RuntimeError
-  attr_reader :name, :path
-
-  def initialize(name, path)
-    @name = name
-    @path = path
-  end
-
-  def to_s
-    "Formula #{name} exists in #{path}"
-  end
-end
-
 class FormulaClassUnavailableError < FormulaUnavailableError
   attr_reader :path
   attr_reader :class_name
diff --git a/Library/Homebrew/historic.rb b/Library/Homebrew/historic.rb
deleted file mode 100644
index 0afb9646ab..0000000000
--- a/Library/Homebrew/historic.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-require "formulary"
-require "tap"
-
-module Homebrew
-  module_function
-
-  # name should not be qualified, since migration of qualified names is already
-  # handled in Formulary::TapLoader.formula_name_path.
-  def search_for_migrated_formula(name, options = {})
-    print_messages = options.fetch(:print_messages, true)
-    migrations = []
-    Tap.each do |old_tap|
-      new_tap_name = old_tap.tap_migrations[name]
-      next unless new_tap_name
-      migrations << [old_tap, new_tap_name]
-      next unless print_messages
-      deprecation = (new_tap_name == "homebrew/boneyard") ? "deprecated " : ""
-      puts "A #{deprecation}formula named \"#{name}\" has been migrated from #{old_tap} to #{new_tap_name}."
-    end
-    migrations
-  end
-
-  # name may be qualified.
-  def search_for_deleted_formula(name, options = {})
-    print_messages = options.fetch(:print_messages, true)
-    warn_shallow = options.fetch(:warn_shallow, false)
-
-    path = Formulary.path name
-    raise FormulaExistsError.new(name, path) if File.exist? path
-    path.to_s =~ HOMEBREW_TAP_PATH_REGEX
-    tap = Tap.new ($1 == "Homebrew" ? "homebrew" : $1), $2.strip_prefix("homebrew-")
-    raise TapUnavailableError, tap.name unless File.exist? tap.path
-    relpath = path.relative_path_from tap.path
-
-    cd tap.path
-
-    if warn_shallow && File.exist?(".git/shallow")
-      opoo <<-EOS.undend
-        The git repository is a shallow clone therefore the output may be incomplete.
-        Use `git fetch -C #{tap.path} --unshallow` to get the full repository.
-      EOS
-    end
-
-    log_cmd = "git log --name-only --max-count=1 --format=$'format:%H\\n%h' -- #{relpath}"
-    hash, hash_abbrev, relpath = Utils.popen_read(log_cmd).lines.map(&:chomp)
-    if hash.to_s.empty? || hash_abbrev.to_s.empty? || relpath.to_s.empty?
-      raise FormulaUnavailableError, name
-    end
-
-    if print_messages
-      puts "#{name} was deleted from #{tap.name} in commit #{hash_abbrev}."
-      puts "Run `brew boneyard #{name}` to show the formula's content prior to its removal."
-    end
-
-    [tap, relpath, hash, hash_abbrev]
-  end
-end
diff --git a/Library/Homebrew/missing_formula.rb b/Library/Homebrew/missing_formula.rb
index 28738a0dc9..ba09f7426b 100644
--- a/Library/Homebrew/missing_formula.rb
+++ b/Library/Homebrew/missing_formula.rb
@@ -6,7 +6,7 @@ module Homebrew
   module MissingFormula
     class << self
       def reason(name)
-        blacklisted_reason(name)
+        blacklisted_reason(name) || tap_migration_reason(name) || deleted_reason(name)
       end
 
       def blacklisted_reason(name)
@@ -100,6 +100,59 @@ module Homebrew
       end
       alias generic_blacklisted_reason blacklisted_reason
 
+      def tap_migration_reason(name)
+        message = nil
+
+        Tap.each do |old_tap|
+          new_tap_name = old_tap.tap_migrations[name]
+          next unless new_tap_name
+          message = <<-EOS.undent
+            It was migrated from #{old_tap} to #{new_tap_name}.
+            You can access it again by running:
+              brew tap #{new_tap_name}
+          EOS
+          break
+        end
+
+        message
+      end
+
+      def deleted_reason(name)
+        path = Formulary.path name
+        return if File.exist? path
+        tap = Tap.from_path(path)
+        return unless File.exist? tap.path
+        relative_path = path.relative_path_from tap.path
+
+        tap.path.cd do
+          # We know this may return incomplete results for shallow clones but
+          # we don't want to nag everyone with a shallow clone to unshallow it.
+          log_command = "git log --name-only --max-count=1 --format=%H\\\\n%h\\\\n%B -- #{relative_path}"
+          hash, short_hash, *commit_message, relative_path =
+            Utils.popen_read(log_command).gsub("\\n", "\n").lines.map(&:chomp)
+          if hash.to_s.empty? || short_hash.to_s.empty? ||
+             relative_path.to_s.empty?
+            return
+          end
+
+          commit_message = commit_message.reject(&:empty?).join("\n  ")
+
+          commit_message.sub!(/ \(#(\d+)\)$/, " (#{tap.issues_url}/\\1)")
+          commit_message.gsub!(/(Closes|Fixes) #(\d+)/, "\\1 #{tap.issues_url}/\\2")
+
+          <<-EOS.undent
+            #{name} was deleted from #{tap.name} in commit #{short_hash}:
+              #{commit_message}
+
+            To show the formula before removal run:
+              git -C "$(brew --repo #{tap})" show #{short_hash}^:#{relative_path}
+
+            If you still use this formula consider creating your own tap:
+              http://docs.brew.sh/How-to-Create-and-Maintain-a-Tap.html
+          EOS
+        end
+      end
+
       require "extend/os/missing_formula"
     end
   end
diff --git a/Library/Homebrew/test/historic_test.rb b/Library/Homebrew/test/historic_test.rb
deleted file mode 100644
index d09656fe0b..0000000000
--- a/Library/Homebrew/test/historic_test.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-require "testing_env"
-require "historic"
-
-class HistoricTest < Homebrew::TestCase
-  def setup
-    super
-
-    @path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo"
-    @path.mkpath
-    @tap = Tap.new("Homebrew", "foo")
-
-    (@path/"tap_migrations.json").write <<-EOS.undent
-      { "migrated-formula": "homebrew/bar" }
-    EOS
-    (@path/"Formula/to-delete.rb").write "placeholder"
-
-    @path.cd do
-      shutup do
-        system "git", "init"
-        system "git", "add", "--all"
-        system "git", "commit", "-m", "initial state"
-        system "git", "rm", "Formula/to-delete.rb"
-        system "git", "commit", "-m", "delete formula 'to-delete'"
-      end
-    end
-  end
-
-  def teardown
-    @path.rmtree
-
-    super
-  end
-
-  def test_search_for_migrated_formula
-    migrations = Homebrew.search_for_migrated_formula("migrated-formula", print_messages: false)
-    assert_equal [[@tap, "homebrew/bar"]], migrations
-  end
-
-  def test_search_for_deleted_formula
-    tap, relpath, hash, = Homebrew.search_for_deleted_formula("homebrew/foo/to-delete",
-                                                              print_messages: false)
-    assert_equal tap, @tap
-    assert_equal relpath, "Formula/to-delete.rb"
-    assert_equal `git rev-parse HEAD`.chomp, hash
-  end
-end
diff --git a/Library/Homebrew/test/missing_formula_spec.rb b/Library/Homebrew/test/missing_formula_spec.rb
index 11b93316fd..f395965a6a 100644
--- a/Library/Homebrew/test/missing_formula_spec.rb
+++ b/Library/Homebrew/test/missing_formula_spec.rb
@@ -1,13 +1,13 @@
 require "missing_formula"
 
 describe Homebrew::MissingFormula do
-  context ".reason" do
+  context "::reason" do
     subject { described_class.reason("gem") }
 
     it { is_expected.to_not be_nil }
   end
 
-  context ".blacklisted_reason" do
+  context "::blacklisted_reason" do
     matcher(:be_blacklisted) do
       match(&Homebrew::MissingFormula.method(:blacklisted_reason))
     end
@@ -122,4 +122,58 @@ describe Homebrew::MissingFormula do
       end
     end
   end
+
+  context "::tap_migration_reason" do
+    subject { described_class.tap_migration_reason(formula) }
+
+    before do
+      Tap.clear_cache
+      tap_path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo"
+      tap_path.mkpath
+      (tap_path/"tap_migrations.json").write <<-EOS.undent
+        { "migrated-formula": "homebrew/bar" }
+      EOS
+    end
+
+    context "with a migrated formula" do
+      let(:formula) { "migrated-formula" }
+      it { is_expected.to_not be_nil }
+    end
+
+    context "with a missing formula" do
+      let(:formula) { "missing-formula" }
+      it { is_expected.to be_nil }
+    end
+  end
+
+  context "::deleted_reason" do
+    subject { described_class.deleted_reason(formula) }
+
+    before do
+      Tap.clear_cache
+      tap_path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo"
+      tap_path.mkpath
+      (tap_path/"deleted-formula.rb").write "placeholder"
+
+      tap_path.cd do
+        shutup do
+          system "git", "init"
+          system "git", "add", "--all"
+          system "git", "commit", "-m", "initial state"
+          system "git", "rm", "deleted-formula.rb"
+          system "git", "commit", "-m", "delete formula 'deleted-formula'"
+        end
+      end
+    end
+
+    context "with a deleted formula" do
+      let(:formula) { "homebrew/foo/deleted-formula" }
+      it { is_expected.to_not be_nil }
+    end
+
+    context "with a formula that never existed" do
+      let(:formula) { "homebrew/foo/missing-formula" }
+      it { is_expected.to be_nil }
+    end
+  end
 end
-- 
GitLab