diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -763,6 +763,7 @@
     'DiffusionLintCountQuery' => 'applications/diffusion/query/DiffusionLintCountQuery.php',
     'DiffusionLintSaveRunner' => 'applications/diffusion/DiffusionLintSaveRunner.php',
     'DiffusionLocalRepositoryFilter' => 'applications/diffusion/data/DiffusionLocalRepositoryFilter.php',
+    'DiffusionLogController' => 'applications/diffusion/controller/DiffusionLogController.php',
     'DiffusionLookSoonConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionLookSoonConduitAPIMethod.php',
     'DiffusionLowLevelCommitFieldsQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelCommitFieldsQuery.php',
     'DiffusionLowLevelCommitQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelCommitQuery.php',
@@ -830,9 +831,11 @@
     'DiffusionPreCommitRefTypeHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefTypeHeraldField.php',
     'DiffusionPreCommitUsesGitLFSHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitUsesGitLFSHeraldField.php',
     'DiffusionPullEventGarbageCollector' => 'applications/diffusion/garbagecollector/DiffusionPullEventGarbageCollector.php',
+    'DiffusionPullLogListController' => 'applications/diffusion/controller/DiffusionPullLogListController.php',
+    'DiffusionPullLogListView' => 'applications/diffusion/view/DiffusionPullLogListView.php',
+    'DiffusionPullLogSearchEngine' => 'applications/diffusion/query/DiffusionPullLogSearchEngine.php',
     'DiffusionPushCapability' => 'applications/diffusion/capability/DiffusionPushCapability.php',
     'DiffusionPushEventViewController' => 'applications/diffusion/controller/DiffusionPushEventViewController.php',
-    'DiffusionPushLogController' => 'applications/diffusion/controller/DiffusionPushLogController.php',
     'DiffusionPushLogListController' => 'applications/diffusion/controller/DiffusionPushLogListController.php',
     'DiffusionPushLogListView' => 'applications/diffusion/view/DiffusionPushLogListView.php',
     'DiffusionPythonExternalSymbolsSource' => 'applications/diffusion/symbol/DiffusionPythonExternalSymbolsSource.php',
@@ -5861,6 +5864,7 @@
     'DiffusionLintCountQuery' => 'PhabricatorQuery',
     'DiffusionLintSaveRunner' => 'Phobject',
     'DiffusionLocalRepositoryFilter' => 'Phobject',
+    'DiffusionLogController' => 'DiffusionController',
     'DiffusionLookSoonConduitAPIMethod' => 'DiffusionConduitAPIMethod',
     'DiffusionLowLevelCommitFieldsQuery' => 'DiffusionLowLevelQuery',
     'DiffusionLowLevelCommitQuery' => 'DiffusionLowLevelQuery',
@@ -5928,10 +5932,12 @@
     'DiffusionPreCommitRefTypeHeraldField' => 'DiffusionPreCommitRefHeraldField',
     'DiffusionPreCommitUsesGitLFSHeraldField' => 'DiffusionPreCommitContentHeraldField',
     'DiffusionPullEventGarbageCollector' => 'PhabricatorGarbageCollector',
+    'DiffusionPullLogListController' => 'DiffusionLogController',
+    'DiffusionPullLogListView' => 'AphrontView',
+    'DiffusionPullLogSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'DiffusionPushCapability' => 'PhabricatorPolicyCapability',
-    'DiffusionPushEventViewController' => 'DiffusionPushLogController',
-    'DiffusionPushLogController' => 'DiffusionController',
-    'DiffusionPushLogListController' => 'DiffusionPushLogController',
+    'DiffusionPushEventViewController' => 'DiffusionLogController',
+    'DiffusionPushLogListController' => 'DiffusionLogController',
     'DiffusionPushLogListView' => 'AphrontView',
     'DiffusionPythonExternalSymbolsSource' => 'DiffusionExternalSymbolsSource',
     'DiffusionQuery' => 'PhabricatorQuery',
diff --git a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php
--- a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php
+++ b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php
@@ -124,6 +124,9 @@
           '(?:query/(?P<queryKey>[^/]+)/)?' => 'DiffusionPushLogListController',
           'view/(?P<id>\d+)/' => 'DiffusionPushEventViewController',
         ),
+        'pulllog/' => array(
+          $this->getQueryRoutePattern() => 'DiffusionPullLogListController',
+        ),
         '(?P<repositoryCallsign>[A-Z]+)' => $repository_routes,
         '(?P<repositoryID>[1-9]\d*)' => $repository_routes,
 
diff --git a/src/applications/diffusion/controller/DiffusionPushLogController.php b/src/applications/diffusion/controller/DiffusionLogController.php
rename from src/applications/diffusion/controller/DiffusionPushLogController.php
rename to src/applications/diffusion/controller/DiffusionLogController.php
--- a/src/applications/diffusion/controller/DiffusionPushLogController.php
+++ b/src/applications/diffusion/controller/DiffusionLogController.php
@@ -1,6 +1,6 @@
 <?php
 
-abstract class DiffusionPushLogController extends DiffusionController {
+abstract class DiffusionLogController extends DiffusionController {
 
   protected function shouldLoadDiffusionRequest() {
     return false;
diff --git a/src/applications/diffusion/controller/DiffusionPullLogListController.php b/src/applications/diffusion/controller/DiffusionPullLogListController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/diffusion/controller/DiffusionPullLogListController.php
@@ -0,0 +1,12 @@
+<?php
+
+final class DiffusionPullLogListController
+  extends DiffusionLogController {
+
+  public function handleRequest(AphrontRequest $request) {
+    return id(new DiffusionPullLogSearchEngine())
+      ->setController($this)
+      ->buildResponse();
+  }
+
+}
diff --git a/src/applications/diffusion/controller/DiffusionPushEventViewController.php b/src/applications/diffusion/controller/DiffusionPushEventViewController.php
--- a/src/applications/diffusion/controller/DiffusionPushEventViewController.php
+++ b/src/applications/diffusion/controller/DiffusionPushEventViewController.php
@@ -1,11 +1,7 @@
 <?php
 
 final class DiffusionPushEventViewController
-  extends DiffusionPushLogController {
-
-  public function shouldAllowPublic() {
-    return true;
-  }
+  extends DiffusionLogController {
 
   public function handleRequest(AphrontRequest $request) {
     $viewer = $this->getViewer();
diff --git a/src/applications/diffusion/controller/DiffusionPushLogListController.php b/src/applications/diffusion/controller/DiffusionPushLogListController.php
--- a/src/applications/diffusion/controller/DiffusionPushLogListController.php
+++ b/src/applications/diffusion/controller/DiffusionPushLogListController.php
@@ -1,10 +1,7 @@
 <?php
 
-final class DiffusionPushLogListController extends DiffusionPushLogController {
-
-  public function shouldAllowPublic() {
-    return true;
-  }
+final class DiffusionPushLogListController
+  extends DiffusionLogController {
 
   public function handleRequest(AphrontRequest $request) {
     return id(new PhabricatorRepositoryPushLogSearchEngine())
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryController.php b/src/applications/diffusion/controller/DiffusionRepositoryController.php
--- a/src/applications/diffusion/controller/DiffusionRepositoryController.php
+++ b/src/applications/diffusion/controller/DiffusionRepositoryController.php
@@ -365,7 +365,7 @@
 
     if ($repository->isHosted()) {
       $push_uri = $this->getApplicationURI(
-        'pushlog/?repositories='.$repository->getMonogram());
+        'pushlog/?repositories='.$repository->getPHID());
 
       $action_view->addAction(
         id(new PhabricatorActionView())
@@ -374,6 +374,15 @@
           ->setHref($push_uri));
     }
 
+    $pull_uri = $this->getApplicationURI(
+      'pulllog/?repositories='.$repository->getPHID());
+
+    $action_view->addAction(
+      id(new PhabricatorActionView())
+        ->setName(pht('View Pull Logs'))
+        ->setIcon('fa-list-alt')
+        ->setHref($pull_uri));
+
     return $action_view;
   }
 
diff --git a/src/applications/diffusion/query/DiffusionPullLogSearchEngine.php b/src/applications/diffusion/query/DiffusionPullLogSearchEngine.php
new file mode 100644
--- /dev/null
+++ b/src/applications/diffusion/query/DiffusionPullLogSearchEngine.php
@@ -0,0 +1,85 @@
+<?php
+
+final class DiffusionPullLogSearchEngine
+  extends PhabricatorApplicationSearchEngine {
+
+  public function getResultTypeDescription() {
+    return pht('Pull Logs');
+  }
+
+  public function getApplicationClassName() {
+    return 'PhabricatorDiffusionApplication';
+  }
+
+  public function newQuery() {
+    return new PhabricatorRepositoryPullEventQuery();
+  }
+
+  protected function buildQueryFromParameters(array $map) {
+    $query = $this->newQuery();
+
+    if ($map['repositoryPHIDs']) {
+      $query->withRepositoryPHIDs($map['repositoryPHIDs']);
+    }
+
+    if ($map['pullerPHIDs']) {
+      $query->withPullerPHIDs($map['pullerPHIDs']);
+    }
+
+    return $query;
+  }
+
+  protected function buildCustomSearchFields() {
+    return array(
+      id(new PhabricatorSearchDatasourceField())
+        ->setDatasource(new DiffusionRepositoryDatasource())
+        ->setKey('repositoryPHIDs')
+        ->setAliases(array('repository', 'repositories', 'repositoryPHID'))
+        ->setLabel(pht('Repositories'))
+        ->setDescription(
+          pht('Search for pull logs for specific repositories.')),
+      id(new PhabricatorUsersSearchField())
+        ->setKey('pullerPHIDs')
+        ->setAliases(array('puller', 'pullers', 'pullerPHID'))
+        ->setLabel(pht('Pullers'))
+        ->setDescription(
+          pht('Search for pull logs by specific users.')),
+    );
+  }
+
+  protected function getURI($path) {
+    return '/diffusion/pulllog/'.$path;
+  }
+
+  protected function getBuiltinQueryNames() {
+    return array(
+      'all' => pht('All Pull Logs'),
+    );
+  }
+
+  public function buildSavedQueryFromBuiltin($query_key) {
+    $query = $this->newSavedQuery();
+    $query->setQueryKey($query_key);
+
+    switch ($query_key) {
+      case 'all':
+        return $query;
+    }
+
+    return parent::buildSavedQueryFromBuiltin($query_key);
+  }
+
+  protected function renderResultList(
+    array $logs,
+    PhabricatorSavedQuery $query,
+    array $handles) {
+
+    $table = id(new DiffusionPullLogListView())
+      ->setViewer($this->requireViewer())
+      ->setLogs($logs);
+
+    return id(new PhabricatorApplicationSearchResultView())
+      ->setTable($table);
+  }
+
+}
diff --git a/src/applications/diffusion/view/DiffusionPullLogListView.php b/src/applications/diffusion/view/DiffusionPullLogListView.php
new file mode 100644
--- /dev/null
+++ b/src/applications/diffusion/view/DiffusionPullLogListView.php
@@ -0,0 +1,115 @@
+<?php
+
+final class DiffusionPullLogListView extends AphrontView {
+
+  private $logs;
+
+  public function setLogs(array $logs) {
+    assert_instances_of($logs, 'PhabricatorRepositoryPullEvent');
+    $this->logs = $logs;
+    return $this;
+  }
+
+  public function render() {
+    $events = $this->logs;
+    $viewer = $this->getViewer();
+
+    $handle_phids = array();
+    foreach ($events as $event) {
+      if ($event->getPullerPHID()) {
+        $handle_phids[] = $event->getPullerPHID();
+      }
+    }
+    $handles = $viewer->loadHandles($handle_phids);
+
+    // Figure out which repositories are editable. We only let you see remote
+    // IPs if you have edit capability on a repository.
+    $editable_repos = array();
+    if ($events) {
+      $editable_repos = id(new PhabricatorRepositoryQuery())
+        ->setViewer($viewer)
+        ->requireCapabilities(
+          array(
+            PhabricatorPolicyCapability::CAN_VIEW,
+            PhabricatorPolicyCapability::CAN_EDIT,
+          ))
+        ->withPHIDs(mpull($events, 'getRepositoryPHID'))
+        ->execute();
+      $editable_repos = mpull($editable_repos, null, 'getPHID');
+    }
+
+    $rows = array();
+    $any_host = false;
+    foreach ($events as $event) {
+      if ($event->getRepositoryPHID()) {
+        $repository = $event->getRepository();
+      } else {
+        $repository = null;
+      }
+
+      // Reveal this if it's valid and the user can edit the repository. For
+      // invalid requests you currently have to go fishing in the database.
+      $remote_address = '-';
+      if ($repository) {
+        if (isset($editable_repos[$event->getRepositoryPHID()])) {
+          $remote_address = $event->getRemoteAddress();
+        }
+      }
+
+      $event_id = $event->getID();
+
+      $repository_link = null;
+      if ($repository) {
+        $repository_link = phutil_tag(
+          'a',
+          array(
+            'href' => $repository->getURI(),
+          ),
+          $repository->getDisplayName());
+      }
+
+      $puller_link = null;
+      if ($event->getPullerPHID()) {
+        $puller_link = $viewer->renderHandle($event->getPullerPHID());
+      }
+
+      $rows[] = array(
+        $event_id,
+        $repository_link,
+        $puller_link,
+        $remote_address,
+        $event->getRemoteProtocolDisplayName(),
+        $event->newResultIcon(),
+        $event->getResultCode(),
+        phabricator_datetime($event->getEpoch(), $viewer),
+      );
+    }
+
+    $table = id(new AphrontTableView($rows))
+      ->setHeaders(
+        array(
+          pht('Pull'),
+          pht('Repository'),
+          pht('Puller'),
+          pht('From'),
+          pht('Via'),
+          null,
+          pht('Error'),
+          pht('Date'),
+        ))
+      ->setColumnClasses(
+        array(
+          'n',
+          '',
+          '',
+          'n',
+          'wide',
+          '',
+          'n',
+          'right',
+        ));
+
+    return $table;
+  }
+
+}
diff --git a/src/applications/repository/query/PhabricatorRepositoryPullEventQuery.php b/src/applications/repository/query/PhabricatorRepositoryPullEventQuery.php
--- a/src/applications/repository/query/PhabricatorRepositoryPullEventQuery.php
+++ b/src/applications/repository/query/PhabricatorRepositoryPullEventQuery.php
@@ -37,19 +37,35 @@
   }
 
   protected function willFilterPage(array $events) {
+    // If a pull targets an invalid repository or fails before authenticating,
+    // it may not have an associated repository.
+
     $repository_phids = mpull($events, 'getRepositoryPHID');
-    $repositories = id(new PhabricatorRepositoryQuery())
-      ->setViewer($this->getViewer())
-      ->withPHIDs($repository_phids)
-      ->execute();
-    $repositories = mpull($repositories, null, 'getPHID');
+    $repository_phids = array_filter($repository_phids);
+
+    if ($repository_phids) {
+      $repositories = id(new PhabricatorRepositoryQuery())
+        ->setViewer($this->getViewer())
+        ->withPHIDs($repository_phids)
+        ->execute();
+      $repositories = mpull($repositories, null, 'getPHID');
+    } else {
+      $repositories = array();
+    }
 
     foreach ($events as $key => $event) {
       $phid = $event->getRepositoryPHID();
+      if (!$phid) {
+        $event->attachRepository(null);
+        continue;
+      }
+
       if (empty($repositories[$phid])) {
         unset($events[$key]);
+        $this->didRejectResult($event);
         continue;
       }
+
       $event->attachRepository($repositories[$phid]);
     }
 
diff --git a/src/applications/repository/storage/PhabricatorRepositoryPullEvent.php b/src/applications/repository/storage/PhabricatorRepositoryPullEvent.php
--- a/src/applications/repository/storage/PhabricatorRepositoryPullEvent.php
+++ b/src/applications/repository/storage/PhabricatorRepositoryPullEvent.php
@@ -51,7 +51,7 @@
       PhabricatorRepositoryPullEventPHIDType::TYPECONST);
   }
 
-  public function attachRepository(PhabricatorRepository $repository) {
+  public function attachRepository(PhabricatorRepository $repository = null) {
     $this->repository = $repository;
     return $this;
   }
@@ -60,6 +60,38 @@
     return $this->assertAttached($this->repository);
   }
 
+  public function getRemoteProtocolDisplayName() {
+    $map = array(
+      'ssh' => pht('SSH'),
+      'http' => pht('HTTP'),
+    );
+
+    $protocol = $this->getRemoteProtocol();
+
+    return idx($map, $protocol, $protocol);
+  }
+
+  public function newResultIcon() {
+    $icon = new PHUIIconView();
+    $type = $this->getResultType();
+
+    switch ($type) {
+      case 'wild':
+        $icon
+          ->setIcon('fa-question indigo')
+          ->setTooltip(pht('Unknown ("%s")', $type));
+        break;
+      case 'pull':
+        $icon
+          ->setIcon('fa-download green')
+          ->setTooltip(pht('Pull'));
+        break;
+    }
+
+    return $icon;
+  }
+
+
 
 /* -(  PhabricatorPolicyInterface  )----------------------------------------- */
 
@@ -71,10 +103,18 @@
   }
 
   public function getPolicy($capability) {
-    return $this->getRepository()->getPolicy($capability);
+    if ($this->getRepository()) {
+      return $this->getRepository()->getPolicy($capability);
+    }
+
+    return PhabricatorPolicies::POLICY_ADMIN;
   }
 
   public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
+    if (!$this->getRepository()) {
+      return false;
+    }
+
     return $this->getRepository()->hasAutomaticCapability($capability, $viewer);
   }