diff --git a/Library/Homebrew/dev-cmd/unbottled.rb b/Library/Homebrew/dev-cmd/unbottled.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f258db70024ce233109fb50bc8349fb1f2bfb7d4
--- /dev/null
+++ b/Library/Homebrew/dev-cmd/unbottled.rb
@@ -0,0 +1,166 @@
+# typed: true
+# frozen_string_literal: true
+
+require "cli/parser"
+require "formula"
+
+module Homebrew
+  extend T::Sig
+
+  module_function
+
+  sig { returns(CLI::Parser) }
+  def unbottled_args
+    Homebrew::CLI::Parser.new do
+      usage_banner <<~EOS
+        `unbottled` [<formula>]
+
+        Outputs the unbottled dependents of formulae.
+      EOS
+      flag "--tag=",
+           description: "Use the specified bottle tag (e.g. big_sur) instead of the current OS."
+      switch "--dependents",
+             description: "Don't get analytics data and sort by number of dependents instead."
+      switch "--total",
+             description: "Output the number of unbottled and total formulae."
+      conflicts "--dependents", "--total"
+    end
+  end
+
+  sig { void }
+  def unbottled
+    args = unbottled_args.parse
+
+    Formulary.enable_factory_cache!
+
+    @bottle_tag = if args.tag.present?
+      args.tag.to_sym
+    else
+      Utils::Bottles.tag
+    end
+
+    if args.named.blank?
+      ohai "Getting formulae..."
+    elsif args.total?
+      raise UsageError, "cannot specify `<formula>` and `--total`."
+    end
+
+    formulae, all_formulae, sort, formula_installs =
+      formulae_all_sort_installs_from_args(args)
+    deps_hash, uses_hash = deps_uses_from_formulae(all_formulae)
+
+    if args.dependents?
+      formula_dependents = {}
+      formulae = formulae.sort_by do |f|
+        dependents = uses_hash[f.name]&.length || 0
+        formula_dependents[f.name] ||= dependents
+      end.reverse
+    end
+
+    if args.total?
+      output_total(formulae)
+      return
+    end
+
+    noun, hash = if args.named.present?
+      [nil, {}]
+    elsif args.dependents?
+      ["dependents", formula_dependents]
+    else
+      ["installs", formula_installs]
+    end
+    output_unbottled(sort, formulae, deps_hash, noun, hash)
+  end
+
+  def formulae_all_sort_installs_from_args(args)
+    if args.named.present?
+      formulae = all_formulae = args.named.to_formulae
+    elsif args.total?
+      formulae = all_formulae = Formula.to_a
+    elsif args.dependents?
+      formulae = all_formulae = Formula.to_a
+
+      sort = " (sorted by installs in the last 90 days)"
+    else
+      formula_installs = {}
+
+      ohai "Getting analytics data..."
+      analytics = Utils::Analytics.formulae_brew_sh_json("analytics/install/90d.json")
+      formulae = analytics["items"].map do |i|
+        f = i["formula"].split.first
+        next if f.include?("/")
+        next if formula_installs[f].present?
+
+        formula_installs[f] = i["count"]
+        begin
+          Formula[f]
+        rescue FormulaUnavailableError
+          nil
+        end
+      end.compact
+      sort = " (sorted by installs in the last 90 days)"
+
+      all_formulae = Formula
+    end
+
+    [formulae, all_formulae, sort, formula_installs]
+  end
+
+  def deps_uses_from_formulae(all_formulae)
+    ohai "Populating dependency tree..."
+
+    deps_hash = {}
+    uses_hash = {}
+
+    all_formulae.each do |f|
+      next unless f.core_formula?
+
+      deps = f.recursive_dependencies do |_, dep|
+        Dependency.prune if dep.optional?
+      end.map(&:to_formula)
+      deps_hash[f.name] = deps
+
+      deps.each do |dep|
+        uses_hash[dep.name] ||= []
+        uses_hash[dep.name] << f
+      end
+    end
+
+    [deps_hash, uses_hash]
+  end
+
+  def output_total(formulae)
+    ohai "Unbottled :#{@bottle_tag} formulae"
+    unbottled_formulae = 0
+
+    formulae.each do |f|
+      next if f.bottle_specification.tag?(@bottle_tag)
+      next if f.bottle_unneeded?
+
+      unbottled_formulae += 1
+    end
+
+    puts "#{unbottled_formulae}/#{formulae.length} remaining."
+  end
+
+  def output_unbottled(sort, formulae, deps_hash, noun, hash)
+    ohai "Unbottled :#{@bottle_tag} dependencies#{sort}"
+    any_found = T.let(false, T::Boolean)
+
+    formulae.each do |f|
+      next if f.bottle_specification.tag?(@bottle_tag)
+
+      deps = Array(deps_hash[f.name]).reject do |dep|
+        dep.bottle_specification.tag?(@bottle_tag) || dep.bottle_unneeded?
+      end
+      next if deps.blank?
+
+      any_found ||= true
+      count = " (#{hash[f.name]} #{noun})" if noun
+      puts "#{f.name}#{count}: #{deps.join(" ")}"
+    end
+    return if any_found
+
+    puts "No unbottled dependencies found!"
+  end
+end
diff --git a/Library/Homebrew/test/dev-cmd/unbottled_spec.rb b/Library/Homebrew/test/dev-cmd/unbottled_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6e54f948b3edebd57f26dc58438db6ba50a786ce
--- /dev/null
+++ b/Library/Homebrew/test/dev-cmd/unbottled_spec.rb
@@ -0,0 +1,8 @@
+# typed: false
+# frozen_string_literal: true
+
+require "cmd/shared_examples/args_parse"
+
+describe "Homebrew.unbottled_args" do
+  it_behaves_like "parseable arguments"
+end
diff --git a/completions/internal_commands_list.txt b/completions/internal_commands_list.txt
index 13cf54e93a95e671672477f10e5126c8c1f8a712..f2c3de6ba2005a92d447397ec1568fe67e70ff4f 100644
--- a/completions/internal_commands_list.txt
+++ b/completions/internal_commands_list.txt
@@ -87,6 +87,7 @@ tc
 test
 tests
 typecheck
+unbottled
 uninstal
 uninstall
 unlink
diff --git a/docs/Manpage.md b/docs/Manpage.md
index 6a9136cc8209800cb7bfaca0345d127e30a36c9d..5af239c2b78f156a3f240fa06453a09acb25c9c8 100644
--- a/docs/Manpage.md
+++ b/docs/Manpage.md
@@ -1278,6 +1278,17 @@ Check for typechecking errors using Sorbet.
 * `--ignore`:
   Ignores input files that contain the given string in their paths (relative to the input path passed to Sorbet).
 
+### `unbottled` [*`formula`*]
+
+Outputs the unbottled dependents of formulae.
+
+* `--tag`:
+  Use the specified bottle tag (e.g. big_sur) instead of the current OS.
+* `--dependents`:
+  Don't get analytics data and sort by number of dependents instead.
+* `--total`:
+  Output the number of unbottled and total formulae.
+
 ### `unpack` [*`options`*] *`formula`*
 
 Unpack the source files for *`formula`* into subdirectories of the current
diff --git a/manpages/brew.1 b/manpages/brew.1
index e0e0cb953f5525a2829a55ca6cf78f72eecf6525..5cdaf4721bf125de361ec176dc128c7b1038f9ad 100644
--- a/manpages/brew.1
+++ b/manpages/brew.1
@@ -1768,6 +1768,21 @@ Typecheck a single file\.
 \fB\-\-ignore\fR
 Ignores input files that contain the given string in their paths (relative to the input path passed to Sorbet)\.
 .
+.SS "\fBunbottled\fR [\fIformula\fR]"
+Outputs the unbottled dependents of formulae\.
+.
+.TP
+\fB\-\-tag\fR
+Use the specified bottle tag (e\.g\. big_sur) instead of the current OS\.
+.
+.TP
+\fB\-\-dependents\fR
+Don\'t get analytics data and sort by number of dependents instead\.
+.
+.TP
+\fB\-\-total\fR
+Output the number of unbottled and total formulae\.
+.
 .SS "\fBunpack\fR [\fIoptions\fR] \fIformula\fR"
 Unpack the source files for \fIformula\fR into subdirectories of the current working directory\.
 .