Report failure if package is included twice in same workspace (#4211)

diff --git a/lib/src/entrypoint.dart b/lib/src/entrypoint.dart
index 861c9df..a9315fb 100644
--- a/lib/src/entrypoint.dart
+++ b/lib/src/entrypoint.dart
@@ -123,6 +123,7 @@
         );
         for (final package in root.transitiveWorkspace) {
           if (identical(pubspecsMet.entries.first.value, package.pubspec)) {
+            validateWorkspaceGraph(root);
             return (root: root, work: package);
           }
         }
diff --git a/lib/src/package.dart b/lib/src/package.dart
index ca6b3f6..e1c5208 100644
--- a/lib/src/package.dart
+++ b/lib/src/package.dart
@@ -359,3 +359,27 @@
     ).map(resolve).toList();
   }
 }
+
+/// Reports an error if the graph of the workspace rooted at [root] is not a
+/// tree.
+void validateWorkspaceGraph(Package root) {
+  final includedFrom = <String, String>{};
+  final stack = [root];
+
+  while (stack.isNotEmpty) {
+    final current = stack.removeLast();
+    for (final child in current.workspaceChildren) {
+      final previous = includedFrom[p.canonicalize(child.dir)];
+      if (previous != null) {
+        fail('''
+Packages can only be included in the workspace once.
+
+`${p.join(child.dir, 'pubspec.yaml')}` is included in the workspace, both from:
+* `${p.join(current.dir, 'pubspec.yaml')}` and
+* ${p.join(previous, 'pubspec.yaml')}.''');
+      }
+      includedFrom[p.canonicalize(child.dir)] = current.dir;
+    }
+    stack.addAll(current.workspaceChildren);
+  }
+}
diff --git a/test/workspace_test.dart b/test/workspace_test.dart
index 967c737..f78c514 100644
--- a/test/workspace_test.dart
+++ b/test/workspace_test.dart
@@ -719,6 +719,45 @@
     expect(unmanagedPackageConfig.statSync().type, FileSystemEntityType.file);
   });
 
+  test('Reports error if workspace doesn\'t form a tree.', () async {
+    await dir(appPath, [
+      libPubspec(
+        'myapp',
+        '1.2.3',
+        sdk: '^3.7.0',
+        extras: {
+          'workspace': ['pkgs/a', 'pkgs'],
+        },
+      ),
+      dir('pkgs', [
+        libPubspec(
+          'a',
+          '1.1.1',
+          resolutionWorkspace: true,
+          extras: {
+            'workspace': ['a'],
+          },
+        ),
+        dir(
+          'a',
+          [
+            libPubspec('a', '1.1.1', resolutionWorkspace: true),
+          ],
+        ),
+      ]),
+    ]).create();
+    final s = p.separator;
+    await pubGet(
+      error: '''
+Packages can only be included in the workspace once.
+
+`.${s}pkgs${s}a${s}pubspec.yaml` is included in the workspace, both from:
+* `.${s}pkgs${s}pubspec.yaml` and
+* .${s}pubspec.yaml.''',
+      environment: {'_PUB_TEST_SDK_VERSION': '3.7.0'},
+    );
+  });
+
   test(
       'Reports a failure if a workspace pubspec is not nested inside the parent dir',
       () async {