diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index fdb170b..2199393 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -4,7 +4,7 @@
   - package-ecosystem: "pub"
     directory: "/"
     schedule:
-      interval: "monthly"
+      interval: "weekly"
 
   - package-ecosystem: github-actions
     directory: /
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index 7f30b9a..f45c8e4 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -24,7 +24,7 @@
       matrix:
         sdk: [dev]
     steps:
-      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
+      - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938
       - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672
         with:
           sdk: ${{ matrix.sdk }}
@@ -52,7 +52,7 @@
         sdk: [dev]
         shard: [0, 1, 2, 3, 4, 5, 6]
     steps:
-      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
+      - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938
       - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672
         with:
           sdk: ${{ matrix.sdk }}
diff --git a/lib/src/command/upgrade.dart b/lib/src/command/upgrade.dart
index 0f2abec..edd7eb6 100644
--- a/lib/src/command/upgrade.dart
+++ b/lib/src/command/upgrade.dart
@@ -71,6 +71,13 @@
     );
 
     argParser.addFlag(
+      'unlock-transitive',
+      help: 'Also upgrades the transitive dependencies '
+          'of the listed [dependencies]',
+      negatable: false,
+    );
+
+    argParser.addFlag(
       'major-versions',
       help: 'Upgrades packages to their latest resolvable versions, '
           'and updates pubspec.yaml.',
@@ -101,11 +108,27 @@
 
   bool get _precompile => argResults.flag('precompile');
 
+  late final Future<List<String>> _packagesToUpgrade =
+      _computePackagesToUpgrade();
+
   /// List of package names to upgrade, if empty then upgrade all packages.
   ///
   /// This allows the user to specify list of names that they want the
   /// upgrade command to affect.
-  List<String> get _packagesToUpgrade => argResults.rest;
+  Future<List<String>> _computePackagesToUpgrade() async {
+    if (argResults.flag('unlock-transitive')) {
+      final graph = await entrypoint.packageGraph;
+      return argResults.rest
+          .expand(
+            (package) =>
+                graph.transitiveDependencies(package).map((p) => p.name),
+          )
+          .toSet()
+          .toList();
+    } else {
+      return argResults.rest;
+    }
+  }
 
   bool get _upgradeNullSafety =>
       argResults.flag('nullsafety') || argResults.flag('null-safety');
@@ -142,7 +165,7 @@
           );
         }
         final changes =
-            entrypoint.tighten(packagesToUpgrade: _packagesToUpgrade);
+            entrypoint.tighten(packagesToUpgrade: await _packagesToUpgrade);
         entrypoint.applyChanges(changes, _dryRun);
       }
     }
@@ -157,7 +180,7 @@
   Future<void> _runUpgrade(Entrypoint e, {bool onlySummary = false}) async {
     await e.acquireDependencies(
       SolveType.upgrade,
-      unlock: _packagesToUpgrade,
+      unlock: await _packagesToUpgrade,
       dryRun: _dryRun,
       precompile: _precompile,
       summaryOnly: onlySummary,
@@ -171,7 +194,7 @@
   /// given.
   ///
   /// This assumes that `--major-versions` was passed.
-  List<String> _directDependenciesToUpgrade() {
+  Future<List<String>> _directDependenciesToUpgrade() async {
     assert(_upgradeMajorVersions);
 
     final directDeps = {
@@ -180,12 +203,13 @@
         ...package.devDependencies.keys,
       ],
     }.toList();
+    final packagesToUpgrade = await _packagesToUpgrade;
     final toUpgrade =
-        _packagesToUpgrade.isEmpty ? directDeps : _packagesToUpgrade;
+        packagesToUpgrade.isEmpty ? directDeps : packagesToUpgrade;
 
     // Check that all package names in upgradeOnly are direct-dependencies
     final notInDeps = toUpgrade.where((n) => !directDeps.contains(n));
-    if (toUpgrade.any(notInDeps.contains)) {
+    if (argResults.rest.any(notInDeps.contains)) {
       usageException('''
 Dependencies specified in `$topLevelProgram pub upgrade --major-versions <dependencies>` must
 be direct 'dependencies' or 'dev_dependencies', following packages are not:
@@ -198,7 +222,7 @@
   }
 
   Future<void> _runUpgradeMajorVersions() async {
-    final toUpgrade = _directDependenciesToUpgrade();
+    final toUpgrade = await _directDependenciesToUpgrade();
     // Solve [resolvablePubspec] in-memory and consolidate the resolved
     // versions of the packages into a map for quick searching.
     final resolvedPackages = <String, PackageId>{};
@@ -267,7 +291,7 @@
         }),
       );
       changes = entrypoint.tighten(
-        packagesToUpgrade: _packagesToUpgrade,
+        packagesToUpgrade: await _packagesToUpgrade,
         existingChanges: changes,
         packageVersions: solveResult.packages,
       );
@@ -279,7 +303,7 @@
     // But without a specific package we want to get as many non-major updates
     // as possible (SolveType.upgrade).
     final solveType =
-        _packagesToUpgrade.isEmpty ? SolveType.upgrade : SolveType.get;
+        (await _packagesToUpgrade).isEmpty ? SolveType.upgrade : SolveType.get;
 
     entrypoint.applyChanges(changes, _dryRun);
     await entrypoint.withUpdatedRootPubspecs({
@@ -290,6 +314,7 @@
       solveType,
       dryRun: _dryRun,
       precompile: !_dryRun && _precompile,
+      unlock: await _packagesToUpgrade,
     );
 
     // If any of the packages to upgrade are dependency overrides, then we
diff --git a/lib/src/entrypoint.dart b/lib/src/entrypoint.dart
index f57aeb9..693c9a2 100644
--- a/lib/src/entrypoint.dart
+++ b/lib/src/entrypoint.dart
@@ -515,7 +515,7 @@
   /// pubspec.yaml and all dependencies downloaded.
   Future<void> acquireDependencies(
     SolveType type, {
-    Iterable<String>? unlock,
+    Iterable<String> unlock = const [],
     bool dryRun = false,
     bool precompile = false,
     bool summaryOnly = false,
@@ -547,7 +547,7 @@
           cache,
           workspaceRoot,
           lockFile: lockFile,
-          unlock: unlock ?? [],
+          unlock: unlock,
         );
       });
     } on SolveFailure catch (e) {
@@ -557,7 +557,7 @@
           this,
           type,
           e.incompatibility,
-          unlock ?? [],
+          unlock,
           cache,
         ),
       );
diff --git a/lib/src/pubspec.dart b/lib/src/pubspec.dart
index fcd9259..1542768 100644
--- a/lib/src/pubspec.dart
+++ b/lib/src/pubspec.dart
@@ -72,6 +72,12 @@
         '`workspace` and `resolution` requires at least language version '
         '${LanguageVersion.firstVersionWithWorkspaces}',
         r.span,
+        hint: '''
+Consider updating the SDK constraint to:
+
+environment:
+  sdk: '^${sdk.version}'
+''',
       );
     }
     if (r == null || r.value == null) return <String>[];
@@ -103,6 +109,12 @@
         '`workspace` and `resolution` requires at least language version '
         '${LanguageVersion.firstVersionWithWorkspaces}',
         r.span,
+        hint: '''
+Consider updating the SDK constraint to:
+
+environment:
+  sdk: '^${sdk.version}'
+''',
       );
     }
     return switch (r?.value) {
@@ -720,8 +732,8 @@
 }
 
 /// Throws a [SourceSpanApplicationException] with the given message.
-Never _error(String message, SourceSpan? span) {
-  throw SourceSpanApplicationException(message, span);
+Never _error(String message, SourceSpan? span, {String? hint}) {
+  throw SourceSpanApplicationException(message, span, hint: hint);
 }
 
 enum _FileType {
diff --git a/pubspec.lock b/pubspec.lock
index 5051dfb..e20fe4d 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -5,23 +5,23 @@
     dependency: transitive
     description:
       name: _fe_analyzer_shared
-      sha256: "45cfa8471b89fb6643fe9bf51bd7931a76b8f5ec2d65de4fb176dba8d4f22c77"
+      sha256: f6dbf021f4b214d85c79822912c5fcd142a2c4869f01222ad371bc51f9f1c356
       url: "https://pub.dev"
     source: hosted
-    version: "73.0.0"
+    version: "74.0.0"
   _macros:
     dependency: transitive
     description: dart
     source: sdk
-    version: "0.3.2"
+    version: "0.3.3"
   analyzer:
     dependency: "direct main"
     description:
       name: analyzer
-      sha256: "4959fec185fe70cce007c57e9ab6983101dbe593d2bf8bbfb4453aaec0cf470a"
+      sha256: f7e8caf82f2d3190881d81012606effdf8a38e6c1ab9e30947149733065f817c
       url: "https://pub.dev"
     source: hosted
-    version: "6.8.0"
+    version: "6.9.0"
   args:
     dependency: "direct main"
     description:
@@ -194,10 +194,10 @@
     dependency: transitive
     description:
       name: macros
-      sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536"
+      sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
       url: "https://pub.dev"
     source: hosted
-    version: "0.1.2-main.4"
+    version: "0.1.3-main.0"
   matcher:
     dependency: transitive
     description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 423b53d..5ff42ae 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -26,7 +26,7 @@
   tar: ^2.0.0
   typed_data: ^1.3.2
   yaml: ^3.1.2
-  yaml_edit: ^2.1.1
+  yaml_edit: ^2.2.1
 
 dev_dependencies:
   checks: ^0.3.0
diff --git a/test/pubspec_test.dart b/test/pubspec_test.dart
index 5db5bf3..8dbff18 100644
--- a/test/pubspec_test.dart
+++ b/test/pubspec_test.dart
@@ -24,6 +24,7 @@
       String contents,
       void Function(Pubspec) fn, {
       String? expectedContains,
+      String? hintContains,
       Description? containingDescription,
     }) {
       var expectation = const TypeMatcher<SourceSpanApplicationException>();
@@ -34,6 +35,13 @@
           contains(expectedContains),
         );
       }
+      if (hintContains != null) {
+        expectation = expectation.having(
+          (error) => error.hint,
+          'hint',
+          contains(hintContains),
+        );
+      }
 
       final pubspec = Pubspec.parse(
         contents,
@@ -384,6 +392,14 @@
 workspace: ['a', 'b', 'c']
 ''',
         (p) => p.workspace,
+        expectedContains: '`workspace` and `resolution` '
+            'requires at least language version 3.5',
+        hintContains: '''
+Consider updating the SDK constraint to:
+
+environment:
+  sdk: '^${Platform.version.split(' ').first}'
+''',
       );
       // but no error if you don't look at it.
       expect(
@@ -406,9 +422,17 @@
         '''
 environment:
   sdk: ^1.2.3
-resolution: local
+resolution: workspace
 ''',
         (p) => p.resolution,
+        expectedContains: '`workspace` and `resolution` '
+            'requires at least language version 3.5',
+        hintContains: '''
+Consider updating the SDK constraint to:
+
+environment:
+  sdk: '^${Platform.version.split(' ').first}'
+''',
       );
     });
 
diff --git a/test/testdata/goldens/help_test/pub upgrade --help.txt b/test/testdata/goldens/help_test/pub upgrade --help.txt
index 2721d13..ade5a8d 100644
--- a/test/testdata/goldens/help_test/pub upgrade --help.txt
+++ b/test/testdata/goldens/help_test/pub upgrade --help.txt
@@ -5,13 +5,14 @@
 Upgrade the current package's dependencies to latest versions.
 
 Usage: pub upgrade [dependencies...]
--h, --help               Print this usage information.
-    --[no-]offline       Use cached packages instead of accessing the network.
--n, --dry-run            Report what dependencies would change but don't change any.
-    --[no-]precompile    Precompile executables in immediate dependencies.
-    --tighten            Updates lower bounds in pubspec.yaml to match the resolved version.
-    --major-versions     Upgrades packages to their latest resolvable versions, and updates pubspec.yaml.
--C, --directory=<dir>    Run this in the directory <dir>.
+-h, --help                 Print this usage information.
+    --[no-]offline         Use cached packages instead of accessing the network.
+-n, --dry-run              Report what dependencies would change but don't change any.
+    --[no-]precompile      Precompile executables in immediate dependencies.
+    --tighten              Updates lower bounds in pubspec.yaml to match the resolved version.
+    --unlock-transitive    Also upgrades the transitive dependencies of the listed [dependencies]
+    --major-versions       Upgrades packages to their latest resolvable versions, and updates pubspec.yaml.
+-C, --directory=<dir>      Run this in the directory <dir>.
 
 Run "pub help" to see global options.
 See https://dart.dev/tools/pub/cmd/pub-upgrade for detailed documentation.
diff --git a/test/upgrade/upgrade_transitive_test.dart b/test/upgrade/upgrade_transitive_test.dart
new file mode 100644
index 0000000..864972b
--- /dev/null
+++ b/test/upgrade/upgrade_transitive_test.dart
@@ -0,0 +1,126 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:test/test.dart';
+
+import '../descriptor.dart' as d;
+import '../test_pub.dart';
+
+void main() {
+  test('without --unlock-transitive, the transitive dependencies stay locked',
+      () async {
+    final server = await servePackages();
+    server.serve('foo', '1.0.0', deps: {'bar': '^1.0.0'});
+    server.serve('bar', '1.0.0');
+
+    await d.appDir(
+      dependencies: {
+        'foo': '^1.0.0',
+      },
+    ).create();
+
+    await pubGet(output: contains('+ foo 1.0.0'));
+
+    server.serve('foo', '1.5.0', deps: {'bar': '^1.0.0'});
+    server.serve('bar', '1.5.0');
+
+    await pubUpgrade(
+      args: ['foo'],
+      output: allOf(
+        contains('> foo 1.5.0'),
+        isNot(contains('> bar')),
+      ),
+    );
+  });
+
+  test('`--unlock-transitive` dependencies gets unlocked', () async {
+    final server = await servePackages();
+    server.serve('foo', '1.0.0', deps: {'bar': '^1.0.0'});
+    server.serve('bar', '1.0.0');
+    server.serve('baz', '1.0.0');
+
+    await d.appDir(
+      dependencies: {
+        'foo': '^1.0.0',
+        'baz': '^1.0.0',
+      },
+    ).create();
+
+    await pubGet(output: contains('+ foo 1.0.0'));
+
+    server.serve('foo', '1.5.0', deps: {'bar': '^1.0.0'});
+    server.serve('bar', '1.5.0');
+    server.serve('baz', '1.5.0');
+
+    await pubUpgrade(
+      args: ['--unlock-transitive', 'foo'],
+      output: allOf(
+        contains('> foo 1.5.0'),
+        contains('> bar 1.5.0'),
+        isNot(
+          contains('baz 1.5.0'),
+        ), // Baz is not a transitive dependency of bar
+      ),
+    );
+  });
+
+  test(
+      '`--major-versions` without `--unlock-transitive` does not allow '
+      'transitive dependencies to be upgraded along with the named packages',
+      () async {
+    final server = await servePackages();
+    server.serve('foo', '1.0.0', deps: {'bar': '^1.0.0'});
+    server.serve('bar', '1.0.0');
+
+    await d.appDir(
+      dependencies: {
+        'foo': '^1.0.0',
+      },
+    ).create();
+
+    await pubGet(output: contains('+ foo 1.0.0'));
+
+    server.serve('foo', '2.0.0', deps: {'bar': '^1.0.0'});
+    server.serve('bar', '1.5.0');
+
+    await pubUpgrade(
+      args: ['--major-versions', 'foo'],
+      output: allOf(
+        contains('> foo 2.0.0'),
+        isNot(contains('bar 1.5.0')),
+      ),
+    );
+  });
+
+  test(
+      '`--unlock-transitive --major-versions` allows transitive dependencies '
+      'be upgraded along with the named packages', () async {
+    final server = await servePackages();
+    server.serve('foo', '1.0.0', deps: {'bar': '^1.0.0'});
+    server.serve('bar', '1.0.0');
+    server.serve('baz', '1.0.0');
+
+    await d.appDir(
+      dependencies: {
+        'foo': '^1.0.0',
+        'baz': '^1.0.0',
+      },
+    ).create();
+
+    await pubGet(output: contains('+ foo 1.0.0'));
+
+    server.serve('foo', '2.0.0', deps: {'bar': '^1.0.0'});
+    server.serve('bar', '1.5.0');
+    server.serve('baz', '1.5.0');
+
+    await pubUpgrade(
+      args: ['--major-versions', '--unlock-transitive', 'foo'],
+      output: allOf(
+        contains('> foo 2.0.0'),
+        contains('> bar 1.5.0'),
+        isNot(contains('> baz 1.5.0')),
+      ),
+    );
+  });
+}