diff --git a/lib/src/command/global.dart b/lib/src/command/global.dart
index 8c2105ca6ef019568f37584970d7f3687d94d8a1..c1b17302f6fd0a2e627a4c943f2b8b01651d2d78 100644
--- a/lib/src/command/global.dart
+++ b/lib/src/command/global.dart
@@ -7,6 +7,7 @@ library pub.command.global;
 import '../command.dart';
 import 'global_activate.dart';
 import 'global_deactivate.dart';
+import 'global_list.dart';
 import 'global_run.dart';
 
 /// Handles the `global` pub command.
@@ -17,6 +18,7 @@ class GlobalCommand extends PubCommand {
   final subcommands = {
     "activate": new GlobalActivateCommand(),
     "deactivate": new GlobalDeactivateCommand(),
+    "list": new GlobalListCommand(),
     "run": new GlobalRunCommand()
   };
 }
diff --git a/lib/src/command/global_list.dart b/lib/src/command/global_list.dart
new file mode 100644
index 0000000000000000000000000000000000000000..c498b42545ba87e2c1ae911866cda2036145b927
--- /dev/null
+++ b/lib/src/command/global_list.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2014, 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.
+
+library pub.command.global_list;
+
+import 'dart:async';
+
+import '../command.dart';
+
+/// Handles the `global list` pub command.
+class GlobalListCommand extends PubCommand {
+  bool get allowTrailingOptions => false;
+  String get description => 'List globally activated packages.';
+  String get usage => 'pub global list';
+
+  Future onRun() {
+    globals.listActivePackages();
+    return null;
+  }
+}
diff --git a/lib/src/global_packages.dart b/lib/src/global_packages.dart
index d762507354f30642062949cdae9e8ac54893b8e6..bbb289a50e740578e00f8e128e92d1fb09d31c51 100644
--- a/lib/src/global_packages.dart
+++ b/lib/src/global_packages.dart
@@ -147,40 +147,29 @@ class GlobalPackages {
         lockFile.serialize(cache.rootDir, cache.sources));
 
     var id = lockFile.packages[package];
-    if (id.source == "git") {
-      var url = GitSource.urlFromDescription(id.description);
-      log.message('Activated ${log.bold(id.name)} ${id.version} from Git '
-          'repository "$url".');
-    } else if (id.source == "path") {
-      var path = PathSource.pathFromDescription(id.description);
-      log.message('Activated ${log.bold(id.name)} ${id.version} at path '
-          '"$path".');
-    } else {
-      log.message("Activated ${log.bold(id.name)} ${id.version}.");
-    }
+    log.message('Activated ${_formatPackage(id)}.');
 
     // TODO(rnystrom): Look in "bin" and display list of binaries that
     // user can run.
   }
 
   /// Shows the user the currently active package with [name], if any.
-  void _describeActive(String package) {
+  void _describeActive(String name) {
     try {
-      var lockFile = new LockFile.load(_getLockFilePath(package),
-          cache.sources);
-      var id = lockFile.packages[package];
+      var lockFile = new LockFile.load(_getLockFilePath(name), cache.sources);
+      var id = lockFile.packages[name];
 
-      if (id.source == "git") {
+      if (id.source == 'git') {
         var url = GitSource.urlFromDescription(id.description);
-        log.message('Package ${log.bold(id.name)} is currently active from '
-            'Git repository "${url}".');
-      } else if (id.source == "path") {
+        log.message('Package ${log.bold(name)} is currently active from Git '
+            'repository "${url}".');
+      } else if (id.source == 'path') {
         var path = PathSource.pathFromDescription(id.description);
-        log.message('Package ${log.bold(package)} is currently active at '
-            'path "$path".');
+        log.message('Package ${log.bold(name)} is currently active at path '
+            '"$path".');
       } else {
-        log.message("Package ${log.bold(package)} is currently active at "
-            "version ${log.bold(id.version)}.");
+        log.message('Package ${log.bold(name)} is currently active at version '
+            '${log.bold(id.version)}.');
       }
     } on IOException catch (error) {
       // If we couldn't read the lock file, it's not activated.
@@ -190,7 +179,7 @@ class GlobalPackages {
 
   /// Deactivates a previously-activated package named [name].
   ///
-  /// If [logDeletion] is true, displays to the user when a package is
+  /// If [logDeactivate] is true, displays to the user when a package is
   /// deactivated. Otherwise, deactivates silently.
   ///
   /// Returns `false` if no package with [name] was currently active.
@@ -204,16 +193,7 @@ class GlobalPackages {
     deleteEntry(lockFilePath);
 
     if (logDeactivate) {
-      if (id.source == "git") {
-        var url = GitSource.urlFromDescription(id.description);
-        log.message('Deactivated package ${log.bold(name)} from Git repository '
-            '"$url".');
-      } else if (id.source == "path") {
-        var path = PathSource.pathFromDescription(id.description);
-        log.message('Deactivated package ${log.bold(name)} at path "$path".');
-      } else {
-        log.message("Deactivated package ${log.bold(name)} ${id.version}.");
-      }
+      log.message('Deactivated package ${_formatPackage(id)}.');
     }
 
     return true;
@@ -258,4 +238,38 @@ class GlobalPackages {
   /// Gets the path to the lock file for an activated cached package with
   /// [name].
   String _getLockFilePath(name) => p.join(_directory, name + ".lock");
+
+  /// Shows to the user formatted list of globally activated packages.
+  void listActivePackages() {
+    if (!dirExists(_directory)) return;
+
+    // Loads lock [file] and returns [PackageId] of the activated package.
+    loadPackageId(file) {
+      var name = p.basenameWithoutExtension(file);
+      var lockFile = new LockFile.load(p.join(_directory, file), cache.sources);
+      return lockFile.packages[name];
+    }
+
+    var packages = listDir(_directory, includeDirs: false)
+        .where((file) => p.extension(file) == '.lock')
+        .map(loadPackageId)
+        .toList();
+
+    packages
+        ..sort((id1, id2) => id1.name.compareTo(id2.name))
+        ..forEach((id) => log.message(_formatPackage(id)));
+  }
+
+  /// Returns formatted string representing the package [id].
+  String _formatPackage(PackageId id) {
+    if (id.source == 'git') {
+      var url = GitSource.urlFromDescription(id.description);
+      return '${log.bold(id.name)} ${id.version} from Git repository "$url"';
+    } else if (id.source == 'path') {
+      var path = PathSource.pathFromDescription(id.description);
+      return '${log.bold(id.name)} ${id.version} at path "$path"';
+    } else {
+      return '${log.bold(id.name)} ${id.version}';
+    }
+  }
 }
diff --git a/test/global/deactivate/git_package_test.dart b/test/global/deactivate/git_package_test.dart
index cc191913b5cb662dec824b80a8e03dbd6f3edcf2..3d14d91eee11ca28741fa1d365914f0ccf224395 100644
--- a/test/global/deactivate/git_package_test.dart
+++ b/test/global/deactivate/git_package_test.dart
@@ -20,6 +20,6 @@ main() {
     schedulePub(args: ["global", "activate", "-sgit", "../foo.git"]);
 
     schedulePub(args: ["global", "deactivate", "foo"],
-        output: 'Deactivated package foo from Git repository "../foo.git".');
+        output: 'Deactivated package foo 1.0.0 from Git repository "../foo.git".');
   });
 }
diff --git a/test/global/deactivate/path_package_test.dart b/test/global/deactivate/path_package_test.dart
index 90047f613d5fed347edf8070f484f8aa08dbe77f..3241efc47f6245c474efed8463f1b70cb136b3c7 100644
--- a/test/global/deactivate/path_package_test.dart
+++ b/test/global/deactivate/path_package_test.dart
@@ -22,6 +22,6 @@ main() {
 
     var path = canonicalize(p.join(sandboxDir, "foo"));
     schedulePub(args: ["global", "deactivate", "foo"],
-        output: 'Deactivated package foo at path "$path".');
+        output: 'Deactivated package foo 1.0.0 at path "$path".');
   });
 }
diff --git a/test/global/list_test.dart b/test/global/list_test.dart
new file mode 100644
index 0000000000000000000000000000000000000000..6286c722d481ac36374e87704bd233324b6eca00
--- /dev/null
+++ b/test/global/list_test.dart
@@ -0,0 +1,76 @@
+// Copyright (c) 2014, 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:path/path.dart' as p;
+
+import '../../lib/src/io.dart';
+import '../descriptor.dart' as d;
+import '../test_pub.dart';
+
+main() {
+  initConfig();
+
+  integration('lists an activated hosted package', () {
+    servePackages([
+      packageMap('foo', '1.0.0')
+    ]);
+
+    schedulePub(args: ['global', 'activate', 'foo']);
+
+    schedulePub(args: ['global', 'list'], output: 'foo 1.0.0');
+  });
+
+  integration('lists an activated Git package', () {
+    ensureGit();
+
+    d.git('foo.git', [
+      d.libPubspec('foo', '1.0.0'),
+      d.dir('bin', [
+        d.file('foo.dart', 'main() => print("ok");')
+      ])
+    ]).create();
+
+    schedulePub(args: ['global', 'activate', '-sgit', '../foo.git']);
+
+    schedulePub(args: ['global', 'list'],
+        output: 'foo 1.0.0 from Git repository "../foo.git"');
+  });
+
+  integration('lists an activated Path package', () {
+    d.dir('foo', [
+      d.libPubspec('foo', '1.0.0'),
+      d.dir('bin', [
+        d.file('foo.dart', 'main() => print("ok");')
+      ])
+    ]).create();
+
+    schedulePub(args: ['global', 'activate', '-spath', '../foo']);
+
+    var path = canonicalize(p.join(sandboxDir, 'foo'));
+    schedulePub(args: ['global', 'list'],
+        output: 'foo 1.0.0 at path "$path"');
+  });
+
+  integration('lists activated packages in alphabetical order', () {
+    servePackages([
+      packageMap('aaa', '1.0.0'),
+      packageMap('bbb', '1.0.0'),
+      packageMap('ccc', '1.0.0')
+    ]);
+
+    schedulePub(args: ['global', 'activate', 'ccc']);
+    schedulePub(args: ['global', 'activate', 'aaa']);
+    schedulePub(args: ['global', 'activate', 'bbb']);
+
+    schedulePub(args: ['global', 'list'], output: '''
+aaa 1.0.0
+bbb 1.0.0
+ccc 1.0.0
+''');
+  });
+
+  integration('lists nothing when no packages activated', () {
+    schedulePub(args: ['global', 'list'], output: '\n');
+  });
+}