diff --git a/src/applications/diffusion/controller/DiffusionCommitController.php b/src/applications/diffusion/controller/DiffusionCommitController.php
index 01740bac8..f572a8640 100644
--- a/src/applications/diffusion/controller/DiffusionCommitController.php
+++ b/src/applications/diffusion/controller/DiffusionCommitController.php
@@ -1,1157 +1,1155 @@
 <?php
 
 final class DiffusionCommitController extends DiffusionController {
 
   const CHANGES_LIMIT = 100;
 
   private $auditAuthorityPHIDs;
   private $highlightedAudits;
 
   public function shouldAllowPublic() {
     return true;
   }
 
   public function willProcessRequest(array $data) {
     // This controller doesn't use blob/path stuff, just pass the dictionary
     // in directly instead of using the AphrontRequest parsing mechanism.
     $data['user'] = $this->getRequest()->getUser();
     $drequest = DiffusionRequest::newFromDictionary($data);
     $this->diffusionRequest = $drequest;
   }
 
   public function processRequest() {
     $drequest = $this->getDiffusionRequest();
     $request = $this->getRequest();
     $user = $request->getUser();
 
     if ($request->getStr('diff')) {
       return $this->buildRawDiffResponse($drequest);
     }
 
     $callsign = $drequest->getRepository()->getCallsign();
 
     $content = array();
     $repository = $drequest->getRepository();
     $commit = $drequest->loadCommit();
 
     $crumbs = $this->buildCrumbs(array(
       'commit' => true,
     ));
 
     if (!$commit) {
       $exists = $this->callConduitWithDiffusionRequest(
         'diffusion.existsquery',
         array('commit' => $drequest->getCommit()));
       if (!$exists) {
         return new Aphront404Response();
       }
 
       $error = id(new AphrontErrorView())
         ->setTitle(pht('Commit Still Parsing'))
         ->appendChild(
           pht(
             'Failed to load the commit because the commit has not been '.
             'parsed yet.'));
 
       return $this->buildApplicationPage(
         array(
           $crumbs,
           $error,
         ),
         array(
           'title' => pht('Commit Still Parsing'),
         ));
     }
 
     $commit_data = $drequest->loadCommitData();
     $commit->attachCommitData($commit_data);
 
     $top_anchor = id(new PhabricatorAnchorView())
       ->setAnchorName('top')
       ->setNavigationMarker(true);
 
     $audit_requests = id(new PhabricatorAuditQuery())
       ->withCommitPHIDs(array($commit->getPHID()))
       ->execute();
     $this->auditAuthorityPHIDs =
       PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($user);
 
     $is_foreign = $commit_data->getCommitDetail('foreign-svn-stub');
     $changesets = null;
     if ($is_foreign) {
       $subpath = $commit_data->getCommitDetail('svn-subpath');
 
       $error_panel = new AphrontErrorView();
       $error_panel->setTitle(pht('Commit Not Tracked'));
       $error_panel->setSeverity(AphrontErrorView::SEVERITY_WARNING);
       $error_panel->appendChild(
         pht("This Diffusion repository is configured to track only one ".
         "subdirectory of the entire Subversion repository, and this commit ".
         "didn't affect the tracked subdirectory ('%s'), so no ".
         "information is available.", $subpath));
       $content[] = $error_panel;
       $content[] = $top_anchor;
     } else {
       $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine();
       $engine->setConfig('viewer', $user);
 
       require_celerity_resource('diffusion-commit-view-css');
       require_celerity_resource('phabricator-remarkup-css');
 
       $parents = $this->callConduitWithDiffusionRequest(
         'diffusion.commitparentsquery',
         array('commit' => $drequest->getCommit()));
 
       if ($parents) {
         $parents = id(new DiffusionCommitQuery())
           ->setViewer($user)
           ->withRepository($repository)
           ->withIdentifiers($parents)
           ->execute();
       }
 
       $headsup_view = id(new PHUIHeaderView())
         ->setHeader(nonempty($commit->getSummary(), pht('Commit Detail')));
 
       $headsup_actions = $this->renderHeadsupActionList($commit, $repository);
 
       $commit_properties = $this->loadCommitProperties(
         $commit,
         $commit_data,
         $parents,
         $audit_requests);
       $property_list = id(new PHUIPropertyListView())
         ->setHasKeyboardShortcuts(true)
         ->setUser($user)
         ->setObject($commit);
       foreach ($commit_properties as $key => $value) {
         $property_list->addProperty($key, $value);
       }
 
       $message = $commit_data->getCommitMessage();
 
       $revision = $commit->getCommitIdentifier();
       $message = $this->linkBugtraq($message);
 
       $message = $engine->markupText($message);
 
       $property_list->invokeWillRenderEvent();
       $property_list->setActionList($headsup_actions);
 
       $detail_list = new PHUIPropertyListView();
       $detail_list->addSectionHeader(
         pht('Description'),
         PHUIPropertyListView::ICON_SUMMARY);
       $detail_list->addTextContent(
         phutil_tag(
           'div',
           array(
             'class' => 'diffusion-commit-message phabricator-remarkup',
           ),
           $message));
       $content[] = $top_anchor;
 
       $object_box = id(new PHUIObjectBoxView())
         ->setHeader($headsup_view)
         ->addPropertyList($property_list)
         ->addPropertyList($detail_list);
 
       $content[] = $object_box;
     }
 
     $content[] = $this->buildComments($commit);
 
     $hard_limit = 1000;
 
     if ($commit->isImported()) {
       $change_query = DiffusionPathChangeQuery::newFromDiffusionRequest(
         $drequest);
       $change_query->setLimit($hard_limit + 1);
       $changes = $change_query->loadChanges();
     } else {
       $changes = array();
     }
 
     $was_limited = (count($changes) > $hard_limit);
     if ($was_limited) {
       $changes = array_slice($changes, 0, $hard_limit);
     }
 
     $content[] = $this->buildMergesTable($commit);
 
     // TODO: This is silly, but the logic to figure out which audits are
     // highlighted currently lives in PhabricatorAuditListView. Refactor this
     // to be less goofy.
     $highlighted_audits = id(new PhabricatorAuditListView())
       ->setAudits($audit_requests)
       ->setAuthorityPHIDs($this->auditAuthorityPHIDs)
       ->setUser($user)
       ->setCommits(array($commit->getPHID() => $commit))
       ->getHighlightedAudits();
 
     $owners_paths = array();
     if ($highlighted_audits) {
       $packages = id(new PhabricatorOwnersPackage())->loadAllWhere(
         'phid IN (%Ls)',
         mpull($highlighted_audits, 'getAuditorPHID'));
       if ($packages) {
         $owners_paths = id(new PhabricatorOwnersPath())->loadAllWhere(
           'repositoryPHID = %s AND packageID IN (%Ld)',
           $repository->getPHID(),
           mpull($packages, 'getID'));
       }
     }
 
     $change_table = new DiffusionCommitChangeTableView();
     $change_table->setDiffusionRequest($drequest);
     $change_table->setPathChanges($changes);
     $change_table->setOwnersPaths($owners_paths);
 
     $count = count($changes);
 
     $bad_commit = null;
     if ($count == 0) {
       $bad_commit = queryfx_one(
         id(new PhabricatorRepository())->establishConnection('r'),
         'SELECT * FROM %T WHERE fullCommitName = %s',
         PhabricatorRepository::TABLE_BADCOMMIT,
         'r'.$callsign.$commit->getCommitIdentifier());
     }
 
     if ($bad_commit) {
       $content[] = $this->renderStatusMessage(
         pht('Bad Commit'),
         $bad_commit['description']);
     } else if ($is_foreign) {
       // Don't render anything else.
     } else if (!$commit->isImported()) {
       $content[] = $this->renderStatusMessage(
         pht('Still Importing...'),
         pht(
           'This commit is still importing. Changes will be visible once '.
           'the import finishes.'));
     } else if (!count($changes)) {
       $content[] = $this->renderStatusMessage(
         pht('Empty Commit'),
         pht(
           'This commit is empty and does not affect any paths.'));
     } else if ($was_limited) {
       $content[] = $this->renderStatusMessage(
         pht('Enormous Commit'),
         pht(
           'This commit is enormous, and affects more than %d files. '.
           'Changes are not shown.',
           $hard_limit));
     } else {
       // The user has clicked "Show All Changes", and we should show all the
       // changes inline even if there are more than the soft limit.
       $show_all_details = $request->getBool('show_all');
 
       $change_panel = new PHUIObjectBoxView();
       $header = new PHUIHeaderView();
       $header->setHeader("Changes (".number_format($count).")");
       $change_panel->setID('toc');
       if ($count > self::CHANGES_LIMIT && !$show_all_details) {
 
         $icon = id(new PHUIIconView())
           ->setSpriteSheet(PHUIIconView::SPRITE_ICONS)
           ->setSpriteIcon('transcript');
 
         $button = id(new PHUIButtonView())
           ->setText(pht('Show All Changes'))
           ->setHref('?show_all=true')
           ->setTag('a')
           ->setIcon($icon);
 
         $warning_view = id(new AphrontErrorView())
           ->setSeverity(AphrontErrorView::SEVERITY_WARNING)
           ->setTitle('Very Large Commit')
           ->appendChild(
             pht("This commit is very large. Load each file individually."));
 
         $change_panel->setErrorView($warning_view);
         $header->addActionLink($button);
       }
 
       $change_panel->appendChild($change_table);
       $change_panel->setHeader($header);
 
       $content[] = $change_panel;
 
       $changesets = DiffusionPathChange::convertToDifferentialChangesets(
         $changes);
 
       $vcs = $repository->getVersionControlSystem();
       switch ($vcs) {
         case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
           $vcs_supports_directory_changes = true;
           break;
         case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
           $vcs_supports_directory_changes = false;
           break;
         default:
           throw new Exception("Unknown VCS.");
       }
 
       $references = array();
       foreach ($changesets as $key => $changeset) {
         $file_type = $changeset->getFileType();
         if ($file_type == DifferentialChangeType::FILE_DIRECTORY) {
           if (!$vcs_supports_directory_changes) {
             unset($changesets[$key]);
             continue;
           }
         }
 
         $references[$key] = $drequest->generateURI(
           array(
             'action' => 'rendering-ref',
             'path'   => $changeset->getFilename(),
           ));
       }
 
       // TODO: Some parts of the views still rely on properties of the
       // DifferentialChangeset. Make the objects ephemeral to make sure we don't
       // accidentally save them, and then set their ID to the appropriate ID for
       // this application (the path IDs).
       $path_ids = array_flip(mpull($changes, 'getPath'));
       foreach ($changesets as $changeset) {
         $changeset->makeEphemeral();
         $changeset->setID($path_ids[$changeset->getFilename()]);
       }
 
       if ($count <= self::CHANGES_LIMIT || $show_all_details) {
         $visible_changesets = $changesets;
       } else {
         $visible_changesets = array();
         $inlines = id(new PhabricatorAuditInlineComment())->loadAllWhere(
           'commitPHID = %s AND (auditCommentID IS NOT NULL OR authorPHID = %s)',
           $commit->getPHID(),
           $user->getPHID());
         $path_ids = mpull($inlines, null, 'getPathID');
         foreach ($changesets as $key => $changeset) {
           if (array_key_exists($changeset->getID(), $path_ids)) {
             $visible_changesets[$key] = $changeset;
           }
         }
       }
 
       $change_list_title = DiffusionView::nameCommit(
         $repository,
         $commit->getCommitIdentifier());
       $change_list = new DifferentialChangesetListView();
       $change_list->setTitle($change_list_title);
       $change_list->setChangesets($changesets);
       $change_list->setVisibleChangesets($visible_changesets);
       $change_list->setRenderingReferences($references);
       $change_list->setRenderURI('/diffusion/'.$callsign.'/diff/');
       $change_list->setRepository($repository);
       $change_list->setUser($user);
-      // pick the first branch for "Browse in Diffusion" View Option
-      $branches     = $commit_data->getCommitDetail('seenOnBranches', array());
-      $first_branch = reset($branches);
-      $change_list->setBranch($first_branch);
+
+      // TODO: Try to setBranch() to something reasonable here?
 
       $change_list->setStandaloneURI(
         '/diffusion/'.$callsign.'/diff/');
       $change_list->setRawFileURIs(
         // TODO: Implement this, somewhat tricky if there's an octopus merge
         // or whatever?
         null,
         '/diffusion/'.$callsign.'/diff/?view=r');
 
       $change_list->setInlineCommentControllerURI(
         '/diffusion/inline/edit/'.phutil_escape_uri($commit->getPHID()).'/');
 
       $change_references = array();
       foreach ($changesets as $key => $changeset) {
         $change_references[$changeset->getID()] = $references[$key];
       }
       $change_table->setRenderingReferences($change_references);
 
       $content[] = $change_list->render();
     }
 
     $content[] = $this->renderAddCommentPanel($commit, $audit_requests);
 
     $commit_id = 'r'.$callsign.$commit->getCommitIdentifier();
     $short_name = DiffusionView::nameCommit(
       $repository,
       $commit->getCommitIdentifier());
 
     $prefs = $user->loadPreferences();
     $pref_filetree = PhabricatorUserPreferences::PREFERENCE_DIFF_FILETREE;
     $pref_collapse = PhabricatorUserPreferences::PREFERENCE_NAV_COLLAPSED;
     $show_filetree = $prefs->getPreference($pref_filetree);
     $collapsed = $prefs->getPreference($pref_collapse);
 
     if ($changesets && $show_filetree) {
       $nav = id(new DifferentialChangesetFileTreeSideNavBuilder())
         ->setAnchorName('top')
         ->setTitle($short_name)
         ->setBaseURI(new PhutilURI('/'.$commit_id))
         ->build($changesets)
         ->setCrumbs($crumbs)
         ->setCollapsed((bool)$collapsed)
         ->appendChild($content);
       $content = $nav;
     } else {
       $content = array($crumbs, $content);
     }
 
     return $this->buildApplicationPage(
       $content,
       array(
         'title' => $commit_id,
         'pageObjects' => array($commit->getPHID()),
       ));
   }
 
   private function loadCommitProperties(
     PhabricatorRepositoryCommit $commit,
     PhabricatorRepositoryCommitData $data,
     array $parents,
     array $audit_requests) {
 
     assert_instances_of($parents, 'PhabricatorRepositoryCommit');
     $viewer = $this->getRequest()->getUser();
     $commit_phid = $commit->getPHID();
     $drequest = $this->getDiffusionRequest();
     $repository = $drequest->getRepository();
 
     $edge_query = id(new PhabricatorEdgeQuery())
       ->withSourcePHIDs(array($commit_phid))
       ->withEdgeTypes(array(
         PhabricatorEdgeConfig::TYPE_COMMIT_HAS_TASK,
         PhabricatorEdgeConfig::TYPE_COMMIT_HAS_PROJECT,
         PhabricatorEdgeConfig::TYPE_COMMIT_HAS_DREV,
       ));
 
     $edges = $edge_query->execute();
 
     $task_phids = array_keys(
       $edges[$commit_phid][PhabricatorEdgeConfig::TYPE_COMMIT_HAS_TASK]);
     $proj_phids = array_keys(
       $edges[$commit_phid][PhabricatorEdgeConfig::TYPE_COMMIT_HAS_PROJECT]);
     $revision_phid = key(
       $edges[$commit_phid][PhabricatorEdgeConfig::TYPE_COMMIT_HAS_DREV]);
 
     $phids = $edge_query->getDestinationPHIDs(array($commit_phid));
 
     if ($data->getCommitDetail('authorPHID')) {
       $phids[] = $data->getCommitDetail('authorPHID');
     }
     if ($data->getCommitDetail('reviewerPHID')) {
       $phids[] = $data->getCommitDetail('reviewerPHID');
     }
     if ($data->getCommitDetail('committerPHID')) {
       $phids[] = $data->getCommitDetail('committerPHID');
     }
     if ($parents) {
       foreach ($parents as $parent) {
         $phids[] = $parent->getPHID();
       }
     }
 
     // NOTE: We should never normally have more than a single push log, but
     // it can occur naturally if a commit is pushed, then the branch it was
     // on is deleted, then the commit is pushed again (or through other similar
     // chains of events). This should be rare, but does not indicate a bug
     // or data issue.
 
     // NOTE: We never query push logs in SVN because the commiter is always
     // the pusher and the commit time is always the push time; the push log
     // is redundant and we save a query by skipping it.
 
     $push_logs = array();
     if ($repository->isHosted() && !$repository->isSVN()) {
       $push_logs = id(new PhabricatorRepositoryPushLogQuery())
         ->setViewer($viewer)
         ->withRepositoryPHIDs(array($repository->getPHID()))
         ->withNewRefs(array($commit->getCommitIdentifier()))
         ->withRefTypes(array(PhabricatorRepositoryPushLog::REFTYPE_COMMIT))
         ->execute();
       foreach ($push_logs as $log) {
         $phids[] = $log->getPusherPHID();
       }
     }
 
     $handles = array();
     if ($phids) {
       $handles = $this->loadViewerHandles($phids);
     }
 
     $props = array();
 
     if ($commit->getAuditStatus()) {
       $status = PhabricatorAuditCommitStatusConstants::getStatusName(
         $commit->getAuditStatus());
       $tag = id(new PHUITagView())
         ->setType(PHUITagView::TYPE_STATE)
         ->setName($status);
 
       switch ($commit->getAuditStatus()) {
         case PhabricatorAuditCommitStatusConstants::NEEDS_AUDIT:
           $tag->setBackgroundColor(PHUITagView::COLOR_ORANGE);
           break;
         case PhabricatorAuditCommitStatusConstants::CONCERN_RAISED:
           $tag->setBackgroundColor(PHUITagView::COLOR_RED);
           break;
         case PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED:
           $tag->setBackgroundColor(PHUITagView::COLOR_BLUE);
           break;
         case PhabricatorAuditCommitStatusConstants::FULLY_AUDITED:
           $tag->setBackgroundColor(PHUITagView::COLOR_GREEN);
           break;
       }
 
       $props['Status'] = $tag;
     }
 
     if ($audit_requests) {
       $user_requests = array();
       $other_requests = array();
       foreach ($audit_requests as $audit_request) {
         if ($audit_request->isUser()) {
           $user_requests[] = $audit_request;
         } else {
           $other_requests[] = $audit_request;
         }
       }
 
       if ($user_requests) {
         $props['Auditors'] = $this->renderAuditStatusView(
           $user_requests);
       }
 
       if ($other_requests) {
         $props['Project/Package Auditors'] = $this->renderAuditStatusView(
           $other_requests);
       }
     }
 
     $author_phid = $data->getCommitDetail('authorPHID');
     $author_name = $data->getAuthorName();
 
     if (!$repository->isSVN()) {
       $authored_info = id(new PHUIStatusItemView());
       // TODO: In Git, a distinct authorship date is available. When present,
       // we should show it here.
 
       if ($author_phid) {
         $authored_info->setTarget($handles[$author_phid]->renderLink());
       } else if (strlen($author_name)) {
         $authored_info->setTarget($author_name);
       }
 
       $props['Authored'] = id(new PHUIStatusListView())
         ->addItem($authored_info);
     }
 
     $committed_info = id(new PHUIStatusItemView())
       ->setNote(phabricator_datetime($commit->getEpoch(), $viewer));
 
     $committer_phid = $data->getCommitDetail('committerPHID');
     $committer_name = $data->getCommitDetail('committer');
     if ($committer_phid) {
       $committed_info->setTarget($handles[$committer_phid]->renderLink());
     } else if (strlen($committer_name)) {
       $committed_info->setTarget($committer_name);
     } else if ($author_phid) {
       $committed_info->setTarget($handles[$author_phid]->renderLink());
     } else if (strlen($author_name)) {
       $committed_info->setTarget($author_name);
     }
 
     $props['Committed'] = id(new PHUIStatusListView())
       ->addItem($committed_info);
 
     if ($push_logs) {
       $pushed_list = new PHUIStatusListView();
 
       foreach ($push_logs as $push_log) {
         $pushed_item = id(new PHUIStatusItemView())
           ->setTarget($handles[$push_log->getPusherPHID()]->renderLink())
           ->setNote(phabricator_datetime($push_log->getEpoch(), $viewer));
         $pushed_list->addItem($pushed_item);
       }
 
       $props['Pushed'] = $pushed_list;
     }
 
     $reviewer_phid = $data->getCommitDetail('reviewerPHID');
     if ($reviewer_phid) {
       $props['Reviewer'] = $handles[$reviewer_phid]->renderLink();
     }
 
     if ($revision_phid) {
       $props['Differential Revision'] = $handles[$revision_phid]->renderLink();
     }
 
     if ($parents) {
       $parent_links = array();
       foreach ($parents as $parent) {
         $parent_links[] = $handles[$parent->getPHID()]->renderLink();
       }
       $props['Parents'] = phutil_implode_html(" \xC2\xB7 ", $parent_links);
     }
 
     $props['Branches'] = phutil_tag(
       'span',
       array(
         'id' => 'commit-branches',
       ),
       pht('Unknown'));
     $props['Tags'] = phutil_tag(
       'span',
       array(
         'id' => 'commit-tags',
       ),
       pht('Unknown'));
 
     $callsign = $repository->getCallsign();
     $root = '/diffusion/'.$callsign.'/commit/'.$commit->getCommitIdentifier();
     Javelin::initBehavior(
       'diffusion-commit-branches',
       array(
         $root.'/branches/' => 'commit-branches',
         $root.'/tags/' => 'commit-tags',
       ));
 
     $refs = $this->buildRefs($drequest);
     if ($refs) {
       $props['References'] = $refs;
     }
 
     if ($task_phids) {
       $task_list = array();
       foreach ($task_phids as $phid) {
         $task_list[] = $handles[$phid]->renderLink();
       }
       $task_list = phutil_implode_html(phutil_tag('br'), $task_list);
       $props['Tasks'] = $task_list;
     }
 
     if ($proj_phids) {
       $proj_list = array();
       foreach ($proj_phids as $phid) {
         $proj_list[] = $handles[$phid]->renderLink();
       }
       $proj_list = phutil_implode_html(phutil_tag('br'), $proj_list);
       $props['Projects'] = $proj_list;
     }
 
     return $props;
   }
 
   private function buildComments(PhabricatorRepositoryCommit $commit) {
     $user = $this->getRequest()->getUser();
     $comments = id(new PhabricatorAuditComment())->loadAllWhere(
       'targetPHID = %s ORDER BY dateCreated ASC',
       $commit->getPHID());
 
     $inlines = id(new PhabricatorAuditInlineComment())->loadAllWhere(
       'commitPHID = %s AND auditCommentID IS NOT NULL',
       $commit->getPHID());
 
     $path_ids = mpull($inlines, 'getPathID');
 
     $path_map = array();
     if ($path_ids) {
       $path_map = id(new DiffusionPathQuery())
         ->withPathIDs($path_ids)
         ->execute();
       $path_map = ipull($path_map, 'path', 'id');
     }
 
     $engine = new PhabricatorMarkupEngine();
     $engine->setViewer($user);
 
     foreach ($comments as $comment) {
       $engine->addObject(
         $comment,
         PhabricatorAuditComment::MARKUP_FIELD_BODY);
     }
 
     foreach ($inlines as $inline) {
       $engine->addObject(
         $inline,
         PhabricatorInlineCommentInterface::MARKUP_FIELD_BODY);
     }
 
     $engine->process();
 
     $view = new DiffusionCommentListView();
     $view->setMarkupEngine($engine);
     $view->setUser($user);
     $view->setComments($comments);
     $view->setInlineComments($inlines);
     $view->setPathMap($path_map);
 
     $phids = $view->getRequiredHandlePHIDs();
     $handles = $this->loadViewerHandles($phids);
     $view->setHandles($handles);
 
     return $view;
   }
 
   private function renderAddCommentPanel(
     PhabricatorRepositoryCommit $commit,
     array $audit_requests) {
     assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest');
 
     $request = $this->getRequest();
     $user = $request->getUser();
 
     if (!$user->isLoggedIn()) {
       return id(new PhabricatorApplicationTransactionCommentView())
         ->setUser($user)
         ->setRequestURI($request->getRequestURI());
     }
 
     $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
 
     $pane_id = celerity_generate_unique_node_id();
     Javelin::initBehavior(
       'differential-keyboard-navigation',
       array(
         'haunt' => $pane_id,
       ));
 
     $draft = id(new PhabricatorDraft())->loadOneWhere(
       'authorPHID = %s AND draftKey = %s',
       $user->getPHID(),
       'diffusion-audit-'.$commit->getID());
     if ($draft) {
       $draft = $draft->getDraft();
     } else {
       $draft = null;
     }
 
     $actions = $this->getAuditActions($commit, $audit_requests);
 
     $form = id(new AphrontFormView())
       ->setUser($user)
       ->setAction('/audit/addcomment/')
       ->addHiddenInput('commit', $commit->getPHID())
       ->appendChild(
         id(new AphrontFormSelectControl())
           ->setLabel(pht('Action'))
           ->setName('action')
           ->setID('audit-action')
           ->setOptions($actions))
       ->appendChild(
         id(new AphrontFormTokenizerControl())
           ->setLabel(pht('Add Auditors'))
           ->setName('auditors')
           ->setControlID('add-auditors')
           ->setControlStyle('display: none')
           ->setID('add-auditors-tokenizer')
           ->setDisableBehavior(true))
       ->appendChild(
         id(new AphrontFormTokenizerControl())
           ->setLabel(pht('Add CCs'))
           ->setName('ccs')
           ->setControlID('add-ccs')
           ->setControlStyle('display: none')
           ->setID('add-ccs-tokenizer')
           ->setDisableBehavior(true))
       ->appendChild(
         id(new PhabricatorRemarkupControl())
           ->setLabel(pht('Comments'))
           ->setName('content')
           ->setValue($draft)
           ->setID('audit-content')
           ->setUser($user))
       ->appendChild(
         id(new AphrontFormSubmitControl())
           ->setValue($is_serious ? pht('Submit') : pht('Cook the Books')));
 
     $header = new PHUIHeaderView();
     $header->setHeader(
       $is_serious ? pht('Audit Commit') : pht('Creative Accounting'));
 
     require_celerity_resource('phabricator-transaction-view-css');
 
     Javelin::initBehavior(
       'differential-add-reviewers-and-ccs',
       array(
         'dynamic' => array(
           'add-auditors-tokenizer' => array(
             'actions' => array('add_auditors' => 1),
             'src' => '/typeahead/common/users/',
             'row' => 'add-auditors',
             'ondemand' => PhabricatorEnv::getEnvConfig('tokenizer.ondemand'),
             'placeholder' => pht('Type a user name...'),
           ),
           'add-ccs-tokenizer' => array(
             'actions' => array('add_ccs' => 1),
             'src' => '/typeahead/common/mailable/',
             'row' => 'add-ccs',
             'ondemand' => PhabricatorEnv::getEnvConfig('tokenizer.ondemand'),
             'placeholder' => pht('Type a user or mailing list...'),
           ),
         ),
         'select' => 'audit-action',
       ));
 
     Javelin::initBehavior('differential-feedback-preview', array(
       'uri'       => '/audit/preview/'.$commit->getID().'/',
       'preview'   => 'audit-preview',
       'content'   => 'audit-content',
       'action'    => 'audit-action',
       'previewTokenizers' => array(
         'auditors' => 'add-auditors-tokenizer',
         'ccs'      => 'add-ccs-tokenizer',
       ),
       'inline'     => 'inline-comment-preview',
       'inlineuri'  => '/diffusion/inline/preview/'.$commit->getPHID().'/',
     ));
 
     $loading = phutil_tag_div(
       'aphront-panel-preview-loading-text',
       pht('Loading preview...'));
 
     $preview_panel = phutil_tag_div(
       'aphront-panel-preview aphront-panel-flush',
       array(
         phutil_tag('div', array('id' => 'audit-preview'), $loading),
         phutil_tag('div', array('id' => 'inline-comment-preview'))
       ));
 
     // TODO: This is pretty awkward, unify the CSS between Diffusion and
     // Differential better.
     require_celerity_resource('differential-core-view-css');
 
     $anchor = id(new PhabricatorAnchorView())
       ->setAnchorName('comment')
       ->setNavigationMarker(true)
       ->render();
 
     $comment_box = id(new PHUIObjectBoxView())
       ->setHeader($header)
       ->appendChild($form);
 
     return phutil_tag(
       'div',
       array(
         'id' => $pane_id,
       ),
       phutil_tag_div(
         'differential-add-comment-panel',
         array($anchor, $comment_box, $preview_panel)));
   }
 
   /**
    * Return a map of available audit actions for rendering into a <select />.
    * This shows the user valid actions, and does not show nonsense/invalid
    * actions (like closing an already-closed commit, or resigning from a commit
    * you have no association with).
    */
   private function getAuditActions(
     PhabricatorRepositoryCommit $commit,
     array $audit_requests) {
     assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest');
     $user = $this->getRequest()->getUser();
 
     $user_is_author = ($commit->getAuthorPHID() == $user->getPHID());
 
     $user_request = null;
     foreach ($audit_requests as $audit_request) {
       if ($audit_request->getAuditorPHID() == $user->getPHID()) {
         $user_request = $audit_request;
         break;
       }
     }
 
     $actions = array();
     $actions[PhabricatorAuditActionConstants::COMMENT] = true;
     $actions[PhabricatorAuditActionConstants::ADD_CCS] = true;
     $actions[PhabricatorAuditActionConstants::ADD_AUDITORS] = true;
 
     // We allow you to accept your own commits. A use case here is that you
     // notice an issue with your own commit and "Raise Concern" as an indicator
     // to other auditors that you're on top of the issue, then later resolve it
     // and "Accept". You can not accept on behalf of projects or packages,
     // however.
     $actions[PhabricatorAuditActionConstants::ACCEPT]  = true;
     $actions[PhabricatorAuditActionConstants::CONCERN] = true;
 
 
     // To resign, a user must have authority on some request and not be the
     // commit's author.
     if (!$user_is_author) {
       $may_resign = false;
 
       $authority_map = array_fill_keys($this->auditAuthorityPHIDs, true);
       foreach ($audit_requests as $request) {
         if (empty($authority_map[$request->getAuditorPHID()])) {
           continue;
         }
         $may_resign = true;
         break;
       }
 
       // If the user has already resigned, don't show "Resign...".
       $status_resigned = PhabricatorAuditStatusConstants::RESIGNED;
       if ($user_request) {
         if ($user_request->getAuditStatus() == $status_resigned) {
           $may_resign = false;
         }
       }
 
       if ($may_resign) {
         $actions[PhabricatorAuditActionConstants::RESIGN] = true;
       }
     }
 
     $status_concern = PhabricatorAuditCommitStatusConstants::CONCERN_RAISED;
     $concern_raised = ($commit->getAuditStatus() == $status_concern);
     $can_close_option = PhabricatorEnv::getEnvConfig(
       'audit.can-author-close-audit');
     if ($can_close_option && $user_is_author && $concern_raised) {
       $actions[PhabricatorAuditActionConstants::CLOSE] = true;
     }
 
     foreach ($actions as $constant => $ignored) {
       $actions[$constant] =
         PhabricatorAuditActionConstants::getActionName($constant);
     }
 
     return $actions;
   }
 
   private function buildMergesTable(PhabricatorRepositoryCommit $commit) {
     $drequest = $this->getDiffusionRequest();
     $limit = 50;
 
     $merges = array();
     try {
       $merges = $this->callConduitWithDiffusionRequest(
         'diffusion.mergedcommitsquery',
         array(
           'commit' => $drequest->getCommit(),
           'limit' => $limit + 1));
     } catch (ConduitException $ex) {
       if ($ex->getMessage() != 'ERR-UNSUPPORTED-VCS') {
         throw $ex;
       }
     }
 
     if (!$merges) {
       return null;
     }
 
     $caption = null;
     if (count($merges) > $limit) {
       $merges = array_slice($merges, 0, $limit);
       $caption =
         "This commit merges more than {$limit} changes. Only the first ".
         "{$limit} are shown.";
     }
 
     $history_table = new DiffusionHistoryTableView();
     $history_table->setUser($this->getRequest()->getUser());
     $history_table->setDiffusionRequest($drequest);
     $history_table->setHistory($merges);
     $history_table->loadRevisions();
 
     $phids = $history_table->getRequiredHandlePHIDs();
     $handles = $this->loadViewerHandles($phids);
     $history_table->setHandles($handles);
 
     $panel = new AphrontPanelView();
     $panel->setHeader(pht('Merged Changes'));
     $panel->setCaption($caption);
     $panel->appendChild($history_table);
     $panel->setNoBackground();
 
     return $panel;
   }
 
   private function renderHeadsupActionList(
     PhabricatorRepositoryCommit $commit,
     PhabricatorRepository $repository) {
 
     $request = $this->getRequest();
     $user = $request->getUser();
 
     $actions = id(new PhabricatorActionListView())
       ->setUser($user)
       ->setObject($commit)
       ->setObjectURI($request->getRequestURI());
 
     $can_edit = PhabricatorPolicyFilter::hasCapability(
       $user,
       $commit,
       PhabricatorPolicyCapability::CAN_EDIT);
 
     $uri = '/diffusion/'.$repository->getCallsign().'/commit/'.
            $commit->getCommitIdentifier().'/edit/';
 
     $action = id(new PhabricatorActionView())
       ->setName(pht('Edit Commit'))
       ->setHref($uri)
       ->setIcon('edit')
       ->setDisabled(!$can_edit)
       ->setWorkflow(!$can_edit);
     $actions->addAction($action);
 
     require_celerity_resource('phabricator-object-selector-css');
     require_celerity_resource('javelin-behavior-phabricator-object-selector');
 
     $maniphest = 'PhabricatorApplicationManiphest';
     if (PhabricatorApplication::isClassInstalled($maniphest)) {
       $action = id(new PhabricatorActionView())
         ->setName(pht('Edit Maniphest Tasks'))
         ->setIcon('attach')
         ->setHref('/search/attach/'.$commit->getPHID().'/TASK/edge/')
         ->setWorkflow(true)
         ->setDisabled(!$can_edit);
       $actions->addAction($action);
     }
 
     $action = id(new PhabricatorActionView())
       ->setName(pht('Download Raw Diff'))
       ->setHref($request->getRequestURI()->alter('diff', true))
       ->setIcon('download');
     $actions->addAction($action);
 
     return $actions;
   }
 
   private function buildRefs(DiffusionRequest $request) {
     // this is git-only, so save a conduit round trip and just get out of
     // here if the repository isn't git
     $type_git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
     $repository = $request->getRepository();
     if ($repository->getVersionControlSystem() != $type_git) {
       return null;
     }
 
     $results = $this->callConduitWithDiffusionRequest(
       'diffusion.refsquery',
       array('commit' => $request->getCommit()));
     $ref_links = array();
     foreach ($results as $ref_data) {
       $ref_links[] = phutil_tag('a',
         array('href' => $ref_data['href']),
         $ref_data['ref']);
     }
     return phutil_implode_html(', ', $ref_links);
   }
 
   private function buildRawDiffResponse(DiffusionRequest $drequest) {
     $raw_diff = $this->callConduitWithDiffusionRequest(
       'diffusion.rawdiffquery',
       array(
         'commit' => $drequest->getCommit(),
         'path' => $drequest->getPath()));
 
     $file = PhabricatorFile::buildFromFileDataOrHash(
       $raw_diff,
       array(
         'name' => $drequest->getCommit().'.diff',
         'ttl' => (60 * 60 * 24),
         'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
       ));
 
     $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
       $file->attachToObject(
         $this->getRequest()->getUser(),
         $drequest->getRepository()->getPHID());
     unset($unguarded);
 
     return id(new AphrontRedirectResponse())->setURI($file->getBestURI());
   }
 
   private function renderAuditStatusView(array $audit_requests) {
     assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest');
 
     $phids = mpull($audit_requests, 'getAuditorPHID');
     $this->loadHandles($phids);
 
     $authority_map = array_fill_keys($this->auditAuthorityPHIDs, true);
 
     $view = new PHUIStatusListView();
     foreach ($audit_requests as $request) {
       $item = new PHUIStatusItemView();
 
       switch ($request->getAuditStatus()) {
         case PhabricatorAuditStatusConstants::AUDIT_NOT_REQUIRED:
           $item->setIcon('open-blue', pht('Commented'));
           break;
         case PhabricatorAuditStatusConstants::AUDIT_REQUIRED:
           $item->setIcon('warning-blue', pht('Audit Required'));
           break;
         case PhabricatorAuditStatusConstants::CONCERNED:
           $item->setIcon('reject-red', pht('Concern Raised'));
           break;
         case PhabricatorAuditStatusConstants::ACCEPTED:
           $item->setIcon('accept-green', pht('Accepted'));
           break;
         case PhabricatorAuditStatusConstants::AUDIT_REQUESTED:
           $item->setIcon('warning-dark', pht('Audit Requested'));
           break;
         case PhabricatorAuditStatusConstants::RESIGNED:
           $item->setIcon('open-dark', pht('Resigned'));
           break;
         case PhabricatorAuditStatusConstants::CLOSED:
           $item->setIcon('accept-blue', pht('Closed'));
           break;
         case PhabricatorAuditStatusConstants::CC:
           $item->setIcon('info-dark', pht('Subscribed'));
           break;
         default:
           $item->setIcon(
             'question-dark',
             pht('%s?', $request->getAuditStatus()));
           break;
       }
 
       $note = array();
       foreach ($request->getAuditReasons() as $reason) {
         $note[] = phutil_tag('div', array(), $reason);
       }
       $item->setNote($note);
 
       $auditor_phid = $request->getAuditorPHID();
       $target = $this->getHandle($auditor_phid)->renderLink();
       $item->setTarget($target);
 
       if (isset($authority_map[$auditor_phid])) {
         $item->setHighlighted(true);
       }
 
       $view->addItem($item);
     }
 
     return $view;
   }
 
   private function linkBugtraq($corpus) {
     $url = PhabricatorEnv::getEnvConfig('bugtraq.url');
     if (!strlen($url)) {
       return $corpus;
     }
 
     $regexes = PhabricatorEnv::getEnvConfig('bugtraq.logregex');
     if (!$regexes) {
       return $corpus;
     }
 
     $parser = id(new PhutilBugtraqParser())
       ->setBugtraqPattern("[[ {$url} | %BUGID% ]]")
       ->setBugtraqCaptureExpression(array_shift($regexes));
 
     $select = array_shift($regexes);
     if ($select) {
       $parser->setBugtraqSelectExpression($select);
     }
 
     return $parser->processCorpus($corpus);
   }
 
 
 }
diff --git a/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php b/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php
index 964ee3421..f9d39b8ea 100644
--- a/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php
+++ b/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php
@@ -1,828 +1,744 @@
 <?php
 
 /**
  * Run pull commands on local working copies to keep them up to date. This
  * daemon handles all repository types.
  *
  * By default, the daemon pulls **every** repository. If you want it to be
  * responsible for only some repositories, you can launch it with a list of
  * PHIDs or callsigns:
  *
  *   ./phd launch repositorypulllocal -- X Q Z
  *
  * You can also launch a daemon which is responsible for all //but// one or
  * more repositories:
  *
  *   ./phd launch repositorypulllocal -- --not A --not B
  *
  * If you have a very large number of repositories and some aren't being pulled
  * as frequently as you'd like, you can either change the pull frequency of
  * the less-important repositories to a larger number (so the daemon will skip
  * them more often) or launch one daemon for all the less-important repositories
  * and one for the more important repositories (or one for each more important
  * repository).
  *
  * @task pull   Pulling Repositories
  * @task git    Git Implementation
  * @task hg     Mercurial Implementation
  */
 final class PhabricatorRepositoryPullLocalDaemon
   extends PhabricatorDaemon {
 
   private $commitCache = array();
   private $repair;
   private $discoveryEngines = array();
 
   public function setRepair($repair) {
     $this->repair = $repair;
     return $this;
   }
 
 
 /* -(  Pulling Repositories  )----------------------------------------------- */
 
 
   /**
    * @task pull
    */
   public function run() {
     $argv = $this->getArgv();
     array_unshift($argv, __CLASS__);
     $args = new PhutilArgumentParser($argv);
     $args->parse(
       array(
         array(
           'name'      => 'no-discovery',
           'help'      => 'Pull only, without discovering commits.',
         ),
         array(
           'name'      => 'not',
           'param'     => 'repository',
           'repeat'    => true,
           'help'      => 'Do not pull __repository__.',
         ),
         array(
           'name'      => 'repositories',
           'wildcard'  => true,
           'help'      => 'Pull specific __repositories__ instead of all.',
         ),
       ));
 
     $no_discovery   = $args->getArg('no-discovery');
     $repo_names     = $args->getArg('repositories');
     $exclude_names  = $args->getArg('not');
 
     // Each repository has an individual pull frequency; after we pull it,
     // wait that long to pull it again. When we start up, try to pull everything
     // serially.
     $retry_after = array();
 
     $min_sleep = 15;
 
     while (true) {
       $repositories = $this->loadRepositories($repo_names);
       if ($exclude_names) {
         $exclude = $this->loadRepositories($exclude_names);
         $repositories = array_diff_key($repositories, $exclude);
       }
 
       // Shuffle the repositories, then re-key the array since shuffle()
       // discards keys. This is mostly for startup, we'll use soft priorities
       // later.
       shuffle($repositories);
       $repositories = mpull($repositories, null, 'getID');
 
       // If any repositories have the NEEDS_UPDATE flag set, pull them
       // as soon as possible.
       $type_need_update = PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE;
       $need_update_messages = id(new PhabricatorRepositoryStatusMessage())
         ->loadAllWhere('statusType = %s', $type_need_update);
       foreach ($need_update_messages as $message) {
         $retry_after[$message->getRepositoryID()] = time();
       }
 
       // If any repositories were deleted, remove them from the retry timer map
       // so we don't end up with a retry timer that never gets updated and
       // causes us to sleep for the minimum amount of time.
       $retry_after = array_select_keys(
         $retry_after,
         array_keys($repositories));
 
       // Assign soft priorities to repositories based on how frequently they
       // should pull again.
       asort($retry_after);
       $repositories = array_select_keys(
         $repositories,
         array_keys($retry_after)) + $repositories;
 
       foreach ($repositories as $id => $repository) {
         $after = idx($retry_after, $id, 0);
         if ($after > time()) {
           continue;
         }
 
         $tracked = $repository->isTracked();
         if (!$tracked) {
           continue;
         }
 
         $callsign = $repository->getCallsign();
 
         try {
           $this->log("Updating repository '{$callsign}'.");
 
           id(new PhabricatorRepositoryPullEngine())
             ->setRepository($repository)
             ->pullRepository();
 
           if (!$no_discovery) {
             // TODO: It would be nice to discover only if we pulled something,
             // but this isn't totally trivial. It's slightly more complicated
             // with hosted repositories, too.
 
             $lock_name = get_class($this).':'.$callsign;
             $lock = PhabricatorGlobalLock::newLock($lock_name);
             $lock->lock();
 
             try {
               $repository->writeStatusMessage(
                 PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE,
                 null);
               $this->discoverRepository($repository);
               $this->updateRepositoryRefs($repository);
               $repository->writeStatusMessage(
                 PhabricatorRepositoryStatusMessage::TYPE_FETCH,
                 PhabricatorRepositoryStatusMessage::CODE_OKAY);
             } catch (Exception $ex) {
               $repository->writeStatusMessage(
                 PhabricatorRepositoryStatusMessage::TYPE_FETCH,
                 PhabricatorRepositoryStatusMessage::CODE_ERROR,
                 array(
                   'message' => pht(
                     'Error updating working copy: %s', $ex->getMessage()),
                 ));
               $lock->unlock();
               throw $ex;
             }
 
             $lock->unlock();
           }
 
           $sleep_for = $repository->getDetail('pull-frequency', $min_sleep);
           $retry_after[$id] = time() + $sleep_for;
         } catch (PhutilLockException $ex) {
           $retry_after[$id] = time() + $min_sleep;
           $this->log("Failed to acquire lock.");
         } catch (Exception $ex) {
           $retry_after[$id] = time() + $min_sleep;
 
           $proxy = new PhutilProxyException(
             "Error while fetching changes to the '{$callsign}' repository.",
             $ex);
           phlog($proxy);
         }
 
         $this->stillWorking();
       }
 
       if ($retry_after) {
         $sleep_until = max(min($retry_after), time() + $min_sleep);
       } else {
         $sleep_until = time() + $min_sleep;
       }
 
       $this->sleep($sleep_until - time());
     }
   }
 
 
   /**
    * @task pull
    */
   protected function loadRepositories(array $names) {
     $query = id(new PhabricatorRepositoryQuery())
       ->setViewer($this->getViewer());
 
     if ($names) {
       $query->withCallsigns($names);
     }
 
     $repos = $query->execute();
 
     if ($names) {
       $by_callsign = mpull($repos, null, 'getCallsign');
       foreach ($names as $name) {
         if (empty($by_callsign[$name])) {
           throw new Exception(
             "No repository exists with callsign '{$name}'!");
         }
       }
     }
 
     return $repos;
   }
 
   public function discoverRepository(PhabricatorRepository $repository) {
     $vcs = $repository->getVersionControlSystem();
 
     $result = null;
     $refs = null;
     switch ($vcs) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         $result = $this->executeGitDiscover($repository);
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         $refs = $this->getDiscoveryEngine($repository)
           ->discoverCommits();
         break;
       default:
         throw new Exception("Unknown VCS '{$vcs}'!");
     }
 
     if ($refs !== null) {
       foreach ($refs as $ref) {
         $this->recordCommit(
           $repository,
           $ref->getIdentifier(),
           $ref->getEpoch(),
-          $ref->getBranch());
+          $close_immediately = true);
       }
     }
 
     $this->checkIfRepositoryIsFullyImported($repository);
 
     try {
       $this->pushToMirrors($repository);
     } catch (Exception $ex) {
       // TODO: We should report these into the UI properly, but for
       // now just complain. These errors are much less severe than
       // pull errors.
       phlog($ex);
     }
 
     if ($refs !== null) {
       return (bool)count($refs);
     } else {
       return $result;
     }
   }
 
   private function updateRepositoryRefs(PhabricatorRepository $repository) {
     id(new PhabricatorRepositoryRefEngine())
       ->setRepository($repository)
       ->updateRefs();
   }
 
   private function getDiscoveryEngine(PhabricatorRepository $repository) {
     $id = $repository->getID();
     if (empty($this->discoveryEngines[$id])) {
       $engine = id(new PhabricatorRepositoryDiscoveryEngine())
           ->setRepository($repository)
           ->setVerbose($this->getVerbose())
           ->setRepairMode($this->repair);
 
       $this->discoveryEngines[$id] = $engine;
     }
     return $this->discoveryEngines[$id];
   }
 
   private function isKnownCommit(
     PhabricatorRepository $repository,
     $target) {
 
     if ($this->getCache($repository, $target)) {
       return true;
     }
 
     if ($this->repair) {
       // In repair mode, rediscover the entire repository, ignoring the
       // database state. We can hit the local cache above, but if we miss it
       // stop the script from going to the database cache.
       return false;
     }
 
     $commit = id(new PhabricatorRepositoryCommit())->loadOneWhere(
       'repositoryID = %d AND commitIdentifier = %s',
       $repository->getID(),
       $target);
 
     if (!$commit) {
       return false;
     }
 
     $this->setCache($repository, $target);
     while (count($this->commitCache) > 2048) {
       array_shift($this->commitCache);
     }
 
     return true;
   }
 
-  private function isKnownCommitOnAnyAutocloseBranch(
-    PhabricatorRepository $repository,
-    $target) {
-
-    $commit = id(new PhabricatorRepositoryCommit())->loadOneWhere(
-      'repositoryID = %d AND commitIdentifier = %s',
-      $repository->getID(),
-      $target);
-
-    if (!$commit) {
-      $callsign = $repository->getCallsign();
-
-      $console = PhutilConsole::getConsole();
-      $console->writeErr(
-        "WARNING: Repository '%s' is missing commits ('%s' is missing from ".
-        "history). Run '%s' to repair the repository.\n",
-        $callsign,
-        $target,
-        "bin/repository discover --repair {$callsign}");
-
-      return false;
-    }
-
-    $data = $commit->loadCommitData();
-    if (!$data) {
-      return false;
-    }
-
-    if ($repository->shouldAutocloseCommit($commit, $data)) {
-      return true;
-    }
-
-    return false;
-  }
-
   private function recordCommit(
     PhabricatorRepository $repository,
     $commit_identifier,
     $epoch,
-    $branch = null) {
+    $close_immediately) {
 
     $commit = new PhabricatorRepositoryCommit();
     $commit->setRepositoryID($repository->getID());
     $commit->setCommitIdentifier($commit_identifier);
     $commit->setEpoch($epoch);
+    if ($close_immediately) {
+      $commit->setImportStatus(PhabricatorRepositoryCommit::IMPORTED_CLOSEABLE);
+    }
 
     $data = new PhabricatorRepositoryCommitData();
-    if ($branch) {
-      $data->setCommitDetail('seenOnBranches', array($branch));
-    }
 
     try {
       $commit->openTransaction();
         $commit->save();
         $data->setCommitID($commit->getID());
         $data->save();
       $commit->saveTransaction();
 
       $this->insertTask($repository, $commit);
 
       queryfx(
         $repository->establishConnection('w'),
         'INSERT INTO %T (repositoryID, size, lastCommitID, epoch)
           VALUES (%d, 1, %d, %d)
           ON DUPLICATE KEY UPDATE
             size = size + 1,
             lastCommitID =
               IF(VALUES(epoch) > epoch, VALUES(lastCommitID), lastCommitID),
             epoch = IF(VALUES(epoch) > epoch, VALUES(epoch), epoch)',
         PhabricatorRepository::TABLE_SUMMARY,
         $repository->getID(),
         $commit->getID(),
         $epoch);
 
       if ($this->repair) {
         // Normally, the query should throw a duplicate key exception. If we
         // reach this in repair mode, we've actually performed a repair.
         $this->log("Repaired commit '{$commit_identifier}'.");
       }
 
       $this->setCache($repository, $commit_identifier);
 
       PhutilEventEngine::dispatchEvent(
         new PhabricatorEvent(
           PhabricatorEventType::TYPE_DIFFUSION_DIDDISCOVERCOMMIT,
           array(
             'repository'  => $repository,
             'commit'      => $commit,
           )));
 
     } catch (AphrontQueryDuplicateKeyException $ex) {
       $commit->killTransaction();
       // Ignore. This can happen because we discover the same new commit
       // more than once when looking at history, or because of races or
       // data inconsistency or cosmic radiation; in any case, we're still
       // in a good state if we ignore the failure.
       $this->setCache($repository, $commit_identifier);
     }
   }
 
-  private function updateCommit(
-    PhabricatorRepository $repository,
-    $commit_identifier,
-    $branch) {
-
-    $commit = id(new PhabricatorRepositoryCommit())->loadOneWhere(
-      'repositoryID = %d AND commitIdentifier = %s',
-      $repository->getID(),
-      $commit_identifier);
-
-    if (!$commit) {
-      // This can happen if the phabricator DB doesn't have the commit info,
-      // or the commit is so big that phabricator couldn't parse it. In this
-      // case we just ignore it.
-      return;
-    }
-
-    $data = id(new PhabricatorRepositoryCommitData())->loadOneWhere(
-      'commitID = %d',
-      $commit->getID());
-    if (!$data) {
-      $data = new PhabricatorRepositoryCommitData();
-      $data->setCommitID($commit->getID());
-    }
-    $branches = $data->getCommitDetail('seenOnBranches', array());
-    $branches[] = $branch;
-    $data->setCommitDetail('seenOnBranches', $branches);
-    $data->save();
-
-    $this->insertTask(
-      $repository,
-      $commit,
-      array(
-        'only' => true
-      ));
-  }
-
   private function insertTask(
     PhabricatorRepository $repository,
     PhabricatorRepositoryCommit $commit,
     $data = array()) {
 
     $vcs = $repository->getVersionControlSystem();
     switch ($vcs) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         $class = 'PhabricatorRepositoryGitCommitMessageParserWorker';
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         $class = 'PhabricatorRepositorySvnCommitMessageParserWorker';
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         $class = 'PhabricatorRepositoryMercurialCommitMessageParserWorker';
         break;
       default:
         throw new Exception("Unknown repository type '{$vcs}'!");
     }
 
     $data['commitID'] = $commit->getID();
 
     PhabricatorWorker::scheduleTask($class, $data);
   }
 
 
   private function setCache(
     PhabricatorRepository $repository,
     $commit_identifier) {
 
     $key = $this->getCacheKey($repository, $commit_identifier);
     $this->commitCache[$key] = true;
   }
 
   private function getCache(
     PhabricatorRepository $repository,
     $commit_identifier) {
 
     $key = $this->getCacheKey($repository, $commit_identifier);
     return idx($this->commitCache, $key, false);
   }
 
   private function getCacheKey(
     PhabricatorRepository $repository,
     $commit_identifier) {
 
     return $repository->getID().':'.$commit_identifier;
   }
 
   private function checkIfRepositoryIsFullyImported(
     PhabricatorRepository $repository) {
 
     // Check if the repository has the "Importing" flag set. We want to clear
     // the flag if we can.
     $importing = $repository->getDetail('importing');
     if (!$importing) {
       // This repository isn't marked as "Importing", so we're done.
       return;
     }
 
     // Look for any commit which hasn't imported.
     $unparsed_commit = queryfx_one(
       $repository->establishConnection('r'),
       'SELECT * FROM %T WHERE repositoryID = %d AND importStatus != %d
         LIMIT 1',
       id(new PhabricatorRepositoryCommit())->getTableName(),
       $repository->getID(),
       PhabricatorRepositoryCommit::IMPORTED_ALL);
     if ($unparsed_commit) {
       // We found a commit which still needs to import, so we can't clear the
       // flag.
       return;
     }
 
     // Clear the "importing" flag.
     $repository->openTransaction();
       $repository->beginReadLocking();
         $repository = $repository->reload();
         $repository->setDetail('importing', false);
         $repository->save();
       $repository->endReadLocking();
     $repository->saveTransaction();
   }
 
 /* -(  Git Implementation  )------------------------------------------------- */
 
 
   /**
    * @task git
    */
   private function executeGitDiscover(
     PhabricatorRepository $repository) {
 
     if (!$repository->isHosted()) {
       $this->verifyOrigin($repository);
     }
 
     $refs = id(new DiffusionLowLevelGitRefQuery())
       ->setRepository($repository)
       ->withIsOriginBranch(true)
       ->execute();
 
     $branches = mpull($refs, 'getCommitIdentifier', 'getShortName');
 
     if (!$branches) {
       // This repository has no branches at all, so we don't need to do
       // anything. Generally, this means the repository is empty.
       return;
     }
 
+    $branches = $this->sortBranches($repository, $branches);
+
     $callsign = $repository->getCallsign();
 
     $tracked_something = false;
 
     $this->log("Discovering commits in repository '{$callsign}'...");
     foreach ($branches as $name => $commit) {
       $this->log("Examining branch '{$name}', at {$commit}.");
       if (!$repository->shouldTrackBranch($name)) {
         $this->log("Skipping, branch is untracked.");
         continue;
       }
 
       $tracked_something = true;
 
       if ($this->isKnownCommit($repository, $commit)) {
         $this->log("Skipping, HEAD is known.");
         continue;
       }
 
       $this->log("Looking for new commits.");
-      $this->executeGitDiscoverCommit($repository, $commit, $name, false);
+      $this->executeGitDiscoverCommit(
+        $repository,
+        $commit,
+        $repository->shouldAutocloseBranch($name));
     }
 
     if (!$tracked_something) {
       $repo_name = $repository->getName();
       $repo_callsign = $repository->getCallsign();
       throw new Exception(
         "Repository r{$repo_callsign} '{$repo_name}' has no tracked branches! ".
         "Verify that your branch filtering settings are correct.");
     }
+  }
 
+  /**
+   * Sort branches so we process closeable branches first. This makes the
+   * whole import process a little cheaper, since we can close these commits
+   * the first time through rather than catching them in the refs step.
+   */
+  private function sortBranches(
+    PhabricatorRepository $repository,
+    array $branches) {
 
-    $this->log("Discovering commits on autoclose branches...");
+    $head_branches = array();
+    $tail_branches = array();
     foreach ($branches as $name => $commit) {
-      $this->log("Examining branch '{$name}', at {$commit}'.");
-      if (!$repository->shouldTrackBranch($name)) {
-        $this->log("Skipping, branch is untracked.");
-        continue;
-      }
-
-      if (!$repository->shouldAutocloseBranch($name)) {
-        $this->log("Skipping, branch is not autoclose.");
-        continue;
-      }
-
-      if ($this->isKnownCommitOnAnyAutocloseBranch($repository, $commit)) {
-        $this->log("Skipping, commit is known on an autoclose branch.");
-        continue;
+      if ($repository->shouldAutocloseBranch($name)) {
+        $head_branches[$name] = $commit;
+      } else {
+        $tail_branches[$name] = $commit;
       }
-
-      $this->log("Looking for new autoclose commits.");
-      $this->executeGitDiscoverCommit($repository, $commit, $name, true);
     }
-  }
 
+    return $head_branches + $tail_branches;
+  }
 
 
   /**
    * @task git
    */
   private function executeGitDiscoverCommit(
     PhabricatorRepository $repository,
     $commit,
-    $branch,
-    $autoclose) {
+    $close_immediately) {
 
     $discover = array($commit);
     $insert = array($commit);
 
     $seen_parent = array();
 
     $stream = new PhabricatorGitGraphStream($repository, $commit);
 
     while (true) {
       $target = array_pop($discover);
       $parents = $stream->getParents($target);
       foreach ($parents as $parent) {
         if (isset($seen_parent[$parent])) {
           // We end up in a loop here somehow when we parse Arcanist if we
           // don't do this. TODO: Figure out why and draw a pretty diagram
           // since it's not evident how parsing a DAG with this causes the
           // loop to stop terminating.
           continue;
         }
         $seen_parent[$parent] = true;
-        if ($autoclose) {
-          $known = $this->isKnownCommitOnAnyAutocloseBranch(
-            $repository,
-            $parent);
-        } else {
-          $known = $this->isKnownCommit($repository, $parent);
-        }
+        $known = $this->isKnownCommit($repository, $parent);
         if (!$known) {
-          $this->log("Discovered commit '{$parent}'.");
+          $this->log(pht('Discovered commit "%s".', $parent));
           $discover[] = $parent;
           $insert[] = $parent;
         }
       }
       if (empty($discover)) {
         break;
       }
     }
 
     $n = count($insert);
-    if ($autoclose) {
-      $this->log("Found {$n} new autoclose commits on branch '{$branch}'.");
-    } else {
-      $this->log("Found {$n} new commits on branch '{$branch}'.");
-    }
+    $this->log(pht('Found %d new commits.', new PhutilNumber($n)));
 
     while (true) {
       $target = array_pop($insert);
       $epoch = $stream->getCommitDate($target);
       $epoch = trim($epoch);
 
-      if ($autoclose) {
-        $this->updateCommit($repository, $target, $branch);
-      } else {
-        $this->recordCommit($repository, $target, $epoch, $branch);
-      }
+      $this->recordCommit($repository, $target, $epoch, $close_immediately);
 
       if (empty($insert)) {
         break;
       }
     }
   }
 
 
   /**
    * Verify that the "origin" remote exists, and points at the correct URI.
    *
    * This catches or corrects some types of misconfiguration, and also repairs
    * an issue where Git 1.7.1 does not create an "origin" for `--bare` clones.
    * See T4041.
    *
    * @param   PhabricatorRepository Repository to verify.
    * @return  void
    */
   private function verifyOrigin(PhabricatorRepository $repository) {
     list($remotes) = $repository->execxLocalCommand(
       'remote show -n origin');
 
     $matches = null;
     if (!preg_match('/^\s*Fetch URL:\s*(.*?)\s*$/m', $remotes, $matches)) {
       throw new Exception(
         "Expected 'Fetch URL' in 'git remote show -n origin'.");
     }
 
     $remote_uri = $matches[1];
     $expect_remote = $repository->getRemoteURI();
 
     if ($remote_uri == "origin") {
       // If a remote does not exist, git pretends it does and prints out a
       // made up remote where the URI is the same as the remote name. This is
       // definitely not correct.
 
       // Possibly, we should use `git remote --verbose` instead, which does not
       // suffer from this problem (but is a little more complicated to parse).
       $valid = false;
       $exists = false;
     } else {
       $normal_type_git = PhabricatorRepositoryURINormalizer::TYPE_GIT;
 
       $remote_normal = id(new PhabricatorRepositoryURINormalizer(
         $normal_type_git,
         $remote_uri))->getNormalizedPath();
 
       $expect_normal = id(new PhabricatorRepositoryURINormalizer(
         $normal_type_git,
         $expect_remote))->getNormalizedPath();
 
       $valid = ($remote_normal == $expect_normal);
       $exists = true;
     }
 
     if (!$valid) {
       if (!$exists) {
         // If there's no "origin" remote, just create it regardless of how
         // strongly we own the working copy. There is almost no conceivable
         // scenario in which this could do damage.
         $this->log(
           pht(
             'Remote "origin" does not exist. Creating "origin", with '.
             'URI "%s".',
             $expect_remote));
         $repository->execxLocalCommand(
           'remote add origin %P',
           $repository->getRemoteURIEnvelope());
 
         // NOTE: This doesn't fetch the origin (it just creates it), so we won't
         // know about origin branches until the next "pull" happens. That's fine
         // for our purposes, but might impact things in the future.
       } else {
         if ($repository->canDestroyWorkingCopy()) {
           // Bad remote, but we can try to repair it.
           $this->log(
             pht(
               'Remote "origin" exists, but is pointed at the wrong URI, "%s". '.
               'Resetting origin URI to "%s.',
               $remote_uri,
               $expect_remote));
           $repository->execxLocalCommand(
             'remote set-url origin %P',
             $repository->getRemoteURIEnvelope());
         } else {
           // Bad remote and we aren't comfortable repairing it.
           $message = pht(
             'Working copy at "%s" has a mismatched origin URI, "%s". '.
             'The expected origin URI is "%s". Fix your configuration, or '.
             'set the remote URI correctly. To avoid breaking anything, '.
             'Phabricator will not automatically fix this.',
             $repository->getLocalPath(),
             $remote_uri,
             $expect_remote);
           throw new Exception($message);
         }
       }
     }
   }
 
   private function pushToMirrors(PhabricatorRepository $repository) {
     if (!$repository->canMirror()) {
       return;
     }
 
     $mirrors = id(new PhabricatorRepositoryMirrorQuery())
       ->setViewer($this->getViewer())
       ->withRepositoryPHIDs(array($repository->getPHID()))
       ->execute();
 
     // TODO: This is a little bit janky, but we don't have first-class
     // infrastructure for running remote commands against an arbitrary remote
     // right now. Just make an emphemeral copy of the repository and muck with
     // it a little bit. In the medium term, we should pull this command stuff
     // out and use it here and for "Land to ...".
 
     $proxy = clone $repository;
     $proxy->makeEphemeral();
 
     $proxy->setDetail('hosting-enabled', false);
     foreach ($mirrors as $mirror) {
       $proxy->setDetail('remote-uri', $mirror->getRemoteURI());
       $proxy->setCredentialPHID($mirror->getCredentialPHID());
 
       $this->log(pht('Pushing to remote "%s"...', $mirror->getRemoteURI()));
 
       if (!$proxy->isGit()) {
         throw new Exception('Unsupported VCS!');
       }
 
       $future = $proxy->getRemoteCommandFuture(
         'push --verbose --mirror -- %P',
         $proxy->getRemoteURIEnvelope());
 
       $future
         ->setCWD($proxy->getLocalPath())
         ->resolvex();
     }
   }
 }
diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php
index ff173b77e..141f7cdff 100644
--- a/src/applications/repository/storage/PhabricatorRepository.php
+++ b/src/applications/repository/storage/PhabricatorRepository.php
@@ -1,1130 +1,1133 @@
 <?php
 
 /**
  * @task uri Repository URI Management
  */
 final class PhabricatorRepository extends PhabricatorRepositoryDAO
   implements
     PhabricatorPolicyInterface,
     PhabricatorFlaggableInterface,
     PhabricatorMarkupInterface {
 
   /**
    * Shortest hash we'll recognize in raw "a829f32" form.
    */
   const MINIMUM_UNQUALIFIED_HASH = 7;
 
   /**
    * Shortest hash we'll recognize in qualified "rXab7ef2f8" form.
    */
   const MINIMUM_QUALIFIED_HASH = 5;
 
   const TABLE_PATH = 'repository_path';
   const TABLE_PATHCHANGE = 'repository_pathchange';
   const TABLE_FILESYSTEM = 'repository_filesystem';
   const TABLE_SUMMARY = 'repository_summary';
   const TABLE_BADCOMMIT = 'repository_badcommit';
   const TABLE_LINTMESSAGE = 'repository_lintmessage';
 
   const SERVE_OFF = 'off';
   const SERVE_READONLY = 'readonly';
   const SERVE_READWRITE = 'readwrite';
 
   protected $name;
   protected $callsign;
   protected $uuid;
   protected $viewPolicy;
   protected $editPolicy;
   protected $pushPolicy;
 
   protected $versionControlSystem;
   protected $details = array();
   protected $credentialPHID;
 
   private $commitCount = self::ATTACHABLE;
   private $mostRecentCommit = self::ATTACHABLE;
   private $projectPHIDs = self::ATTACHABLE;
 
   public static function initializeNewRepository(PhabricatorUser $actor) {
     $app = id(new PhabricatorApplicationQuery())
       ->setViewer($actor)
       ->withClasses(array('PhabricatorApplicationDiffusion'))
       ->executeOne();
 
     $view_policy = $app->getPolicy(DiffusionCapabilityDefaultView::CAPABILITY);
     $edit_policy = $app->getPolicy(DiffusionCapabilityDefaultEdit::CAPABILITY);
     $push_policy = $app->getPolicy(DiffusionCapabilityDefaultPush::CAPABILITY);
 
     return id(new PhabricatorRepository())
       ->setViewPolicy($view_policy)
       ->setEditPolicy($edit_policy)
       ->setPushPolicy($push_policy);
   }
 
   public function getConfiguration() {
     return array(
       self::CONFIG_AUX_PHID => true,
       self::CONFIG_SERIALIZATION => array(
         'details' => self::SERIALIZATION_JSON,
       ),
     ) + parent::getConfiguration();
   }
 
   public function generatePHID() {
     return PhabricatorPHID::generateNewPHID(
       PhabricatorRepositoryPHIDTypeRepository::TYPECONST);
   }
 
   public function toDictionary() {
     return array(
       'name'        => $this->getName(),
       'phid'        => $this->getPHID(),
       'callsign'    => $this->getCallsign(),
       'vcs'         => $this->getVersionControlSystem(),
       'uri'         => PhabricatorEnv::getProductionURI($this->getURI()),
       'remoteURI'   => (string)$this->getRemoteURI(),
       'tracking'    => $this->getDetail('tracking-enabled'),
       'description' => $this->getDetail('description'),
     );
   }
 
   public function getDetail($key, $default = null) {
     return idx($this->details, $key, $default);
   }
 
   public function getHumanReadableDetail($key, $default = null) {
     $value = $this->getDetail($key, $default);
 
     switch ($key) {
       case 'branch-filter':
       case 'close-commits-filter':
         $value = array_keys($value);
         $value = implode(', ', $value);
         break;
     }
 
     return $value;
   }
 
   public function setDetail($key, $value) {
     $this->details[$key] = $value;
     return $this;
   }
 
   public function attachCommitCount($count) {
     $this->commitCount = $count;
     return $this;
   }
 
   public function getCommitCount() {
     return $this->assertAttached($this->commitCount);
   }
 
   public function attachMostRecentCommit(
     PhabricatorRepositoryCommit $commit = null) {
     $this->mostRecentCommit = $commit;
     return $this;
   }
 
   public function getMostRecentCommit() {
     return $this->assertAttached($this->mostRecentCommit);
   }
 
   public function getDiffusionBrowseURIForPath(
     PhabricatorUser $user,
     $path,
     $line = null,
     $branch = null) {
 
     $drequest = DiffusionRequest::newFromDictionary(
       array(
         'user' => $user,
         'repository' => $this,
         'path' => $path,
         'branch' => $branch,
       ));
 
     return $drequest->generateURI(
       array(
         'action' => 'browse',
         'line'   => $line,
       ));
   }
 
   public function getLocalPath() {
     return $this->getDetail('local-path');
   }
 
   public function getSubversionBaseURI($commit = null) {
     $subpath = $this->getDetail('svn-subpath');
     if (!strlen($subpath)) {
       $subpath = null;
     }
     return $this->getSubversionPathURI($subpath, $commit);
   }
 
   public function getSubversionPathURI($path = null, $commit = null) {
     $vcs = $this->getVersionControlSystem();
     if ($vcs != PhabricatorRepositoryType::REPOSITORY_TYPE_SVN) {
       throw new Exception("Not a subversion repository!");
     }
 
     if ($this->isHosted()) {
       $uri = 'file://'.$this->getLocalPath();
     } else {
       $uri = $this->getDetail('remote-uri');
     }
 
     $uri = rtrim($uri, '/');
 
     if (strlen($path)) {
       $path = rawurlencode($path);
       $path = str_replace('%2F', '/', $path);
       $uri = $uri.'/'.ltrim($path, '/');
     }
 
     if ($path !== null || $commit !== null) {
       $uri .= '@';
     }
 
     if ($commit !== null) {
       $uri .= $commit;
     }
 
     return $uri;
   }
 
   public function attachProjectPHIDs(array $project_phids) {
     $this->projectPHIDs = $project_phids;
     return $this;
   }
 
   public function getProjectPHIDs() {
     return $this->assertAttached($this->projectPHIDs);
   }
 
 
 /* -(  Remote Command Execution  )------------------------------------------- */
 
 
   public function execRemoteCommand($pattern /* , $arg, ... */) {
     $args = func_get_args();
     return $this->newRemoteCommandFuture($args)->resolve();
   }
 
   public function execxRemoteCommand($pattern /* , $arg, ... */) {
     $args = func_get_args();
     return $this->newRemoteCommandFuture($args)->resolvex();
   }
 
   public function getRemoteCommandFuture($pattern /* , $arg, ... */) {
     $args = func_get_args();
     return $this->newRemoteCommandFuture($args);
   }
 
   public function passthruRemoteCommand($pattern /* , $arg, ... */) {
     $args = func_get_args();
     return $this->newRemoteCommandPassthru($args)->execute();
   }
 
   private function newRemoteCommandFuture(array $argv) {
     $argv = $this->formatRemoteCommand($argv);
     $future = newv('ExecFuture', $argv);
     $future->setEnv($this->getRemoteCommandEnvironment());
     return $future;
   }
 
   private function newRemoteCommandPassthru(array $argv) {
     $argv = $this->formatRemoteCommand($argv);
     $passthru = newv('PhutilExecPassthru', $argv);
     $passthru->setEnv($this->getRemoteCommandEnvironment());
     return $passthru;
   }
 
 
 /* -(  Local Command Execution  )-------------------------------------------- */
 
 
   public function execLocalCommand($pattern /* , $arg, ... */) {
     $args = func_get_args();
     return $this->newLocalCommandFuture($args)->resolve();
   }
 
   public function execxLocalCommand($pattern /* , $arg, ... */) {
     $args = func_get_args();
     return $this->newLocalCommandFuture($args)->resolvex();
   }
 
   public function getLocalCommandFuture($pattern /* , $arg, ... */) {
     $args = func_get_args();
     return $this->newLocalCommandFuture($args);
   }
 
   public function passthruLocalCommand($pattern /* , $arg, ... */) {
     $args = func_get_args();
     return $this->newLocalCommandPassthru($args)->execute();
   }
 
   private function newLocalCommandFuture(array $argv) {
     $this->assertLocalExists();
 
     $argv = $this->formatLocalCommand($argv);
     $future = newv('ExecFuture', $argv);
     $future->setEnv($this->getLocalCommandEnvironment());
 
     if ($this->usesLocalWorkingCopy()) {
       $future->setCWD($this->getLocalPath());
     }
 
     return $future;
   }
 
   private function newLocalCommandPassthru(array $argv) {
     $this->assertLocalExists();
 
     $argv = $this->formatLocalCommand($argv);
     $future = newv('PhutilExecPassthru', $argv);
     $future->setEnv($this->getLocalCommandEnvironment());
 
     if ($this->usesLocalWorkingCopy()) {
       $future->setCWD($this->getLocalPath());
     }
 
     return $future;
   }
 
 
 /* -(  Command Infrastructure  )--------------------------------------------- */
 
 
   private function getSSHWrapper() {
     $root = dirname(phutil_get_library_root('phabricator'));
     return $root.'/bin/ssh-connect';
   }
 
   private function getCommonCommandEnvironment() {
     $env = array(
       // NOTE: Force the language to "en_US.UTF-8", which overrides locale
       // settings. This makes stuff print in English instead of, e.g., French,
       // so we can parse the output of some commands, error messages, etc.
       'LANG' => 'en_US.UTF-8',
 
       // Propagate PHABRICATOR_ENV explicitly. For discussion, see T4155.
       'PHABRICATOR_ENV' => PhabricatorEnv::getSelectedEnvironmentName(),
     );
 
     switch ($this->getVersionControlSystem()) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         // NOTE: See T2965. Some time after Git 1.7.5.4, Git started fataling if
         // it can not read $HOME. For many users, $HOME points at /root (this
         // seems to be a default result of Apache setup). Instead, explicitly
         // point $HOME at a readable, empty directory so that Git looks for the
         // config file it's after, fails to locate it, and moves on. This is
         // really silly, but seems like the least damaging approach to
         // mitigating the issue.
 
         $root = dirname(phutil_get_library_root('phabricator'));
         $env['HOME'] = $root.'/support/empty/';
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         // NOTE: This overrides certain configuration, extensions, and settings
         // which make Mercurial commands do random unusual things.
         $env['HGPLAIN'] = 1;
         break;
       default:
         throw new Exception("Unrecognized version control system.");
     }
 
     return $env;
   }
 
   private function getLocalCommandEnvironment() {
     return $this->getCommonCommandEnvironment();
   }
 
   private function getRemoteCommandEnvironment() {
     $env = $this->getCommonCommandEnvironment();
 
     if ($this->shouldUseSSH()) {
       // NOTE: This is read by `bin/ssh-connect`, and tells it which credentials
       // to use.
       $env['PHABRICATOR_CREDENTIAL'] = $this->getCredentialPHID();
       switch ($this->getVersionControlSystem()) {
         case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
           // Force SVN to use `bin/ssh-connect`.
           $env['SVN_SSH'] = $this->getSSHWrapper();
           break;
         case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
           // Force Git to use `bin/ssh-connect`.
           $env['GIT_SSH'] = $this->getSSHWrapper();
           break;
         case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
           // We force Mercurial through `bin/ssh-connect` too, but it uses a
           // command-line flag instead of an environmental variable.
           break;
         default:
           throw new Exception("Unrecognized version control system.");
       }
     }
 
     return $env;
   }
 
   private function formatRemoteCommand(array $args) {
     $pattern = $args[0];
     $args = array_slice($args, 1);
 
     switch ($this->getVersionControlSystem()) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         if ($this->shouldUseHTTP() || $this->shouldUseSVNProtocol()) {
           $flags = array();
           $flag_args = array();
           $flags[] = '--non-interactive';
           $flags[] = '--no-auth-cache';
           if ($this->shouldUseHTTP()) {
             $flags[] = '--trust-server-cert';
           }
 
           $credential_phid = $this->getCredentialPHID();
           if ($credential_phid) {
             $key = PassphrasePasswordKey::loadFromPHID(
               $credential_phid,
               PhabricatorUser::getOmnipotentUser());
             $flags[] = '--username %P';
             $flags[] = '--password %P';
             $flag_args[] = $key->getUsernameEnvelope();
             $flag_args[] = $key->getPasswordEnvelope();
           }
 
           $flags = implode(' ', $flags);
           $pattern = "svn {$flags} {$pattern}";
           $args = array_mergev(array($flag_args, $args));
         } else {
           $pattern = "svn --non-interactive {$pattern}";
         }
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         $pattern = "git {$pattern}";
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         if ($this->shouldUseSSH()) {
           $pattern = "hg --config ui.ssh=%s {$pattern}";
           array_unshift(
             $args,
             $this->getSSHWrapper());
         } else {
           $pattern = "hg {$pattern}";
         }
         break;
       default:
         throw new Exception("Unrecognized version control system.");
     }
 
     array_unshift($args, $pattern);
 
     return $args;
   }
 
   private function formatLocalCommand(array $args) {
     $pattern = $args[0];
     $args = array_slice($args, 1);
 
     switch ($this->getVersionControlSystem()) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         $pattern = "svn --non-interactive {$pattern}";
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         $pattern = "git {$pattern}";
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         $pattern = "hg {$pattern}";
         break;
       default:
         throw new Exception("Unrecognized version control system.");
     }
 
     array_unshift($args, $pattern);
 
     return $args;
   }
 
   /**
    * Sanitize output of an `hg` command invoked with the `--debug` flag to make
    * it usable.
    *
    * @param string Output from `hg --debug ...`
    * @return string Usable output.
    */
   public static function filterMercurialDebugOutput($stdout) {
     // When hg commands are run with `--debug` and some config file isn't
     // trusted, Mercurial prints out a warning to stdout, twice, after Feb 2011.
     //
     // http://selenic.com/pipermail/mercurial-devel/2011-February/028541.html
 
     $lines = preg_split('/(?<=\n)/', $stdout);
     $regex = '/ignoring untrusted configuration option .*\n$/';
 
     foreach ($lines as $key => $line) {
       $lines[$key] = preg_replace($regex, '', $line);
     }
 
     return implode('', $lines);
   }
 
   public function getURI() {
     return '/diffusion/'.$this->getCallsign().'/';
   }
 
   public function isTracked() {
     return $this->getDetail('tracking-enabled', false);
   }
 
   public function getDefaultBranch() {
     $default = $this->getDetail('default-branch');
     if (strlen($default)) {
       return $default;
     }
 
     $default_branches = array(
       PhabricatorRepositoryType::REPOSITORY_TYPE_GIT        => 'master',
       PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL  => 'default',
     );
 
     return idx($default_branches, $this->getVersionControlSystem());
   }
 
   public function getDefaultArcanistBranch() {
     return coalesce($this->getDefaultBranch(), 'svn');
   }
 
   private function isBranchInFilter($branch, $filter_key) {
     $vcs = $this->getVersionControlSystem();
 
     $is_git = ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT);
 
     $use_filter = ($is_git);
 
     if ($use_filter) {
       $filter = $this->getDetail($filter_key, array());
       if ($filter && empty($filter[$branch])) {
         return false;
       }
     }
 
     // By default, all branches pass.
     return true;
   }
 
   public function shouldTrackBranch($branch) {
     return $this->isBranchInFilter($branch, 'branch-filter');
   }
 
   public function shouldAutocloseBranch($branch) {
     if ($this->isImporting()) {
       return false;
     }
 
     if ($this->getDetail('disable-autoclose', false)) {
       return false;
     }
 
     return $this->isBranchInFilter($branch, 'close-commits-filter');
   }
 
   public function shouldAutocloseCommit(
     PhabricatorRepositoryCommit $commit,
     PhabricatorRepositoryCommitData $data) {
 
     if ($this->getDetail('disable-autoclose', false)) {
       return false;
     }
 
     switch ($this->getVersionControlSystem()) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         return true;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         return true;
       default:
         throw new Exception("Unrecognized version control system.");
     }
 
     $closeable_flag = PhabricatorRepositoryCommit::IMPORTED_CLOSEABLE;
     if ($commit->isPartiallyImported($closeable_flag)) {
       return true;
     }
 
+    // TODO: Remove this eventually, it's no longer written to by the import
+    // pipeline (however, old tasks may still be queued which don't reflect
+    // the new data format).
     $branches = $data->getCommitDetail('seenOnBranches', array());
     foreach ($branches as $branch) {
       if ($this->shouldAutocloseBranch($branch)) {
         return true;
       }
     }
 
     return false;
   }
 
   public function formatCommitName($commit_identifier) {
     $vcs = $this->getVersionControlSystem();
 
     $type_git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
     $type_hg = PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL;
 
     $is_git = ($vcs == $type_git);
     $is_hg = ($vcs == $type_hg);
     if ($is_git || $is_hg) {
       $short_identifier = substr($commit_identifier, 0, 12);
     } else {
       $short_identifier = $commit_identifier;
     }
 
     return 'r'.$this->getCallsign().$short_identifier;
   }
 
   public function isImporting() {
     return (bool)$this->getDetail('importing', false);
   }
 
 /* -(  Repository URI Management  )------------------------------------------ */
 
 
   /**
    * Get the remote URI for this repository.
    *
    * @return string
    * @task uri
    */
   public function getRemoteURI() {
     return (string)$this->getRemoteURIObject();
   }
 
 
   /**
    * Get the remote URI for this repository, including credentials if they're
    * used by this repository.
    *
    * @return PhutilOpaqueEnvelope URI, possibly including credentials.
    * @task uri
    */
   public function getRemoteURIEnvelope() {
     $uri = $this->getRemoteURIObject();
 
     $remote_protocol = $this->getRemoteProtocol();
     if ($remote_protocol == 'http' || $remote_protocol == 'https') {
       // For SVN, we use `--username` and `--password` flags separately, so
       // don't add any credentials here.
       if (!$this->isSVN()) {
         $credential_phid = $this->getCredentialPHID();
         if ($credential_phid) {
           $key = PassphrasePasswordKey::loadFromPHID(
             $credential_phid,
             PhabricatorUser::getOmnipotentUser());
 
           $uri->setUser($key->getUsernameEnvelope()->openEnvelope());
           $uri->setPass($key->getPasswordEnvelope()->openEnvelope());
         }
       }
     }
 
     return new PhutilOpaqueEnvelope((string)$uri);
   }
 
 
   /**
    * Get the remote URI for this repository, without authentication information.
    *
    * @return string Repository URI.
    * @task uri
    */
   public function getPublicRemoteURI() {
     $uri = $this->getRemoteURIObject();
 
     // Make sure we don't leak anything if this repo is using HTTP Basic Auth
     // with the credentials in the URI or something zany like that.
 
     // If repository is not accessed over SSH we remove both username and
     // password.
     if (!$this->shouldUseSSH()) {
       $uri->setUser(null);
 
       // This might be a Git URI or a normal URI. If it's Git, there's no
       // password support.
       if ($uri instanceof PhutilURI) {
         $uri->setPass(null);
       }
     }
 
     return (string)$uri;
   }
 
 
   /**
    * Get the protocol for the repository's remote.
    *
    * @return string Protocol, like "ssh" or "git".
    * @task uri
    */
   public function getRemoteProtocol() {
     $uri = $this->getRemoteURIObject();
 
     if ($uri instanceof PhutilGitURI) {
       return 'ssh';
     } else {
       return $uri->getProtocol();
     }
   }
 
 
   /**
    * Get a parsed object representation of the repository's remote URI. This
    * may be a normal URI (returned as a @{class@libphutil:PhutilURI}) or a git
    * URI (returned as a @{class@libphutil:PhutilGitURI}).
    *
    * @return wild A @{class@libphutil:PhutilURI} or
    *              @{class@libphutil:PhutilGitURI}.
    * @task uri
    */
   public function getRemoteURIObject() {
     $raw_uri = $this->getDetail('remote-uri');
     if (!$raw_uri) {
       return new PhutilURI('');
     }
 
     if (!strncmp($raw_uri, '/', 1)) {
       return new PhutilURI('file://'.$raw_uri);
     }
 
     $uri = new PhutilURI($raw_uri);
     if ($uri->getProtocol()) {
       return $uri;
     }
 
     $uri = new PhutilGitURI($raw_uri);
     if ($uri->getDomain()) {
       return $uri;
     }
 
     throw new Exception("Remote URI '{$raw_uri}' could not be parsed!");
   }
 
 
   /**
    * Determine if we should connect to the remote using SSH flags and
    * credentials.
    *
    * @return bool True to use the SSH protocol.
    * @task uri
    */
   private function shouldUseSSH() {
     if ($this->isHosted()) {
       return false;
     }
 
     $protocol = $this->getRemoteProtocol();
     if ($this->isSSHProtocol($protocol)) {
       return true;
     }
 
     return false;
   }
 
 
   /**
    * Determine if we should connect to the remote using HTTP flags and
    * credentials.
    *
    * @return bool True to use the HTTP protocol.
    * @task uri
    */
   private function shouldUseHTTP() {
     if ($this->isHosted()) {
       return false;
     }
 
     $protocol = $this->getRemoteProtocol();
     return ($protocol == 'http' || $protocol == 'https');
   }
 
 
   /**
    * Determine if we should connect to the remote using SVN flags and
    * credentials.
    *
    * @return bool True to use the SVN protocol.
    * @task uri
    */
   private function shouldUseSVNProtocol() {
     if ($this->isHosted()) {
       return false;
     }
 
     $protocol = $this->getRemoteProtocol();
     return ($protocol == 'svn');
   }
 
 
   /**
    * Determine if a protocol is SSH or SSH-like.
    *
    * @param string A protocol string, like "http" or "ssh".
    * @return bool True if the protocol is SSH-like.
    * @task uri
    */
   private function isSSHProtocol($protocol) {
     return ($protocol == 'ssh' || $protocol == 'svn+ssh');
   }
 
   public function delete() {
     $this->openTransaction();
 
       $paths = id(new PhabricatorOwnersPath())
         ->loadAllWhere('repositoryPHID = %s', $this->getPHID());
       foreach ($paths as $path) {
         $path->delete();
       }
 
       $projects = id(new PhabricatorRepositoryArcanistProject())
         ->loadAllWhere('repositoryID = %d', $this->getID());
       foreach ($projects as $project) {
         // note each project deletes its PhabricatorRepositorySymbols
         $project->delete();
       }
 
       $commits = id(new PhabricatorRepositoryCommit())
         ->loadAllWhere('repositoryID = %d', $this->getID());
       foreach ($commits as $commit) {
         // note PhabricatorRepositoryAuditRequests and
         // PhabricatorRepositoryCommitData are deleted here too.
         $commit->delete();
       }
 
       $mirrors = id(new PhabricatorRepositoryMirror())
         ->loadAllWhere('repositoryPHID = %s', $this->getPHID());
       foreach ($mirrors as $mirror) {
         $mirror->delete();
       }
 
       $ref_cursors = id(new PhabricatorRepositoryRefCursor())
         ->loadAllWhere('repositoryPHID = %s', $this->getPHID());
       foreach ($ref_cursors as $cursor) {
         $cursor->delete();
       }
 
       $conn_w = $this->establishConnection('w');
 
       queryfx(
         $conn_w,
         'DELETE FROM %T WHERE repositoryID = %d',
         self::TABLE_FILESYSTEM,
         $this->getID());
 
       queryfx(
         $conn_w,
         'DELETE FROM %T WHERE repositoryID = %d',
         self::TABLE_PATHCHANGE,
         $this->getID());
 
       queryfx(
         $conn_w,
         'DELETE FROM %T WHERE repositoryID = %d',
         self::TABLE_SUMMARY,
         $this->getID());
 
       $result = parent::delete();
 
     $this->saveTransaction();
     return $result;
   }
 
   public function isGit() {
     $vcs = $this->getVersionControlSystem();
     return ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT);
   }
 
   public function isSVN() {
     $vcs = $this->getVersionControlSystem();
     return ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_SVN);
   }
 
   public function isHg() {
     $vcs = $this->getVersionControlSystem();
     return ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL);
   }
 
   public function isHosted() {
     return (bool)$this->getDetail('hosting-enabled', false);
   }
 
   public function setHosted($enabled) {
     return $this->setDetail('hosting-enabled', $enabled);
   }
 
   public function getServeOverHTTP() {
     if ($this->isSVN()) {
       return self::SERVE_OFF;
     }
     $serve = $this->getDetail('serve-over-http', self::SERVE_OFF);
     return $this->normalizeServeConfigSetting($serve);
   }
 
   public function setServeOverHTTP($mode) {
     return $this->setDetail('serve-over-http', $mode);
   }
 
   public function getServeOverSSH() {
     $serve = $this->getDetail('serve-over-ssh', self::SERVE_OFF);
     return $this->normalizeServeConfigSetting($serve);
   }
 
   public function setServeOverSSH($mode) {
     return $this->setDetail('serve-over-ssh', $mode);
   }
 
   public static function getProtocolAvailabilityName($constant) {
     switch ($constant) {
       case self::SERVE_OFF:
         return pht('Off');
       case self::SERVE_READONLY:
         return pht('Read Only');
       case self::SERVE_READWRITE:
         return pht('Read/Write');
       default:
         return pht('Unknown');
     }
   }
 
   private function normalizeServeConfigSetting($value) {
     switch ($value) {
       case self::SERVE_OFF:
       case self::SERVE_READONLY:
         return $value;
       case self::SERVE_READWRITE:
         if ($this->isHosted()) {
           return self::SERVE_READWRITE;
         } else {
           return self::SERVE_READONLY;
         }
       default:
         return self::SERVE_OFF;
     }
   }
 
 
   /**
    * Raise more useful errors when there are basic filesystem problems.
    */
   private function assertLocalExists() {
     if (!$this->usesLocalWorkingCopy()) {
       return;
     }
 
     $local = $this->getLocalPath();
     Filesystem::assertExists($local);
     Filesystem::assertIsDirectory($local);
     Filesystem::assertReadable($local);
   }
 
   /**
    * Determine if the working copy is bare or not. In Git, this corresponds
    * to `--bare`. In Mercurial, `--noupdate`.
    */
   public function isWorkingCopyBare() {
     switch ($this->getVersionControlSystem()) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         return false;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         $local = $this->getLocalPath();
         if (Filesystem::pathExists($local.'/.git')) {
           return false;
         } else {
           return true;
         }
     }
   }
 
   public function usesLocalWorkingCopy() {
     switch ($this->getVersionControlSystem()) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         return $this->isHosted();
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         return true;
     }
   }
 
   public function getHookDirectories() {
     $directories = array();
     if (!$this->isHosted()) {
       return $directories;
     }
 
     $root = $this->getLocalPath();
 
     switch ($this->getVersionControlSystem()) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         if ($this->isWorkingCopyBare()) {
           $directories[] = $root.'/hooks/pre-receive-phabricator.d/';
         } else {
           $directories[] = $root.'/.git/hooks/pre-receive-phabricator.d/';
         }
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         $directories[] = $root.'/hooks/pre-commit-phabricator.d/';
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         // NOTE: We don't support custom Mercurial hooks for now because they're
         // messy and we can't easily just drop a `hooks.d/` directory next to
         // the hooks.
         break;
     }
 
     return $directories;
   }
 
   public function canDestroyWorkingCopy() {
     if ($this->isHosted()) {
       // Never destroy hosted working copies.
       return false;
     }
 
     $default_path = PhabricatorEnv::getEnvConfig(
       'repository.default-local-path');
     return Filesystem::isDescendant($this->getLocalPath(), $default_path);
   }
 
   public function canMirror() {
     if (!$this->isHosted()) {
       return false;
     }
 
     if ($this->isGit()) {
       return true;
     }
 
     return false;
   }
 
   public function canAllowDangerousChanges() {
     if (!$this->isHosted()) {
       return false;
     }
 
     if ($this->isGit()) {
       return true;
     }
 
     return false;
   }
 
   public function shouldAllowDangerousChanges() {
     return (bool)$this->getDetail('allow-dangerous-changes');
   }
 
   public function writeStatusMessage(
     $status_type,
     $status_code,
     array $parameters = array()) {
 
     $table = new PhabricatorRepositoryStatusMessage();
     $conn_w = $table->establishConnection('w');
     $table_name = $table->getTableName();
 
     if ($status_code === null) {
       queryfx(
         $conn_w,
         'DELETE FROM %T WHERE repositoryID = %d AND statusType = %s',
         $table_name,
         $this->getID(),
         $status_type);
     } else {
       queryfx(
         $conn_w,
         'INSERT INTO %T
           (repositoryID, statusType, statusCode, parameters, epoch)
           VALUES (%d, %s, %s, %s, %d)
           ON DUPLICATE KEY UPDATE
             statusCode = VALUES(statusCode),
             parameters = VALUES(parameters),
             epoch = VALUES(epoch)',
         $table_name,
         $this->getID(),
         $status_type,
         $status_code,
         json_encode($parameters),
         time());
     }
 
     return $this;
   }
 
 
 /* -(  PhabricatorPolicyInterface  )----------------------------------------- */
 
 
   public function getCapabilities() {
     return array(
       PhabricatorPolicyCapability::CAN_VIEW,
       PhabricatorPolicyCapability::CAN_EDIT,
       DiffusionCapabilityPush::CAPABILITY,
     );
   }
 
   public function getPolicy($capability) {
     switch ($capability) {
       case PhabricatorPolicyCapability::CAN_VIEW:
         return $this->getViewPolicy();
       case PhabricatorPolicyCapability::CAN_EDIT:
         return $this->getEditPolicy();
       case DiffusionCapabilityPush::CAPABILITY:
         return $this->getPushPolicy();
     }
   }
 
   public function hasAutomaticCapability($capability, PhabricatorUser $user) {
     return false;
   }
 
   public function describeAutomaticCapability($capability) {
     return null;
   }
 
 
 
 /* -(  PhabricatorMarkupInterface  )----------------------------------------- */
 
 
   public function getMarkupFieldKey($field) {
     $hash = PhabricatorHash::digestForIndex($this->getMarkupText($field));
     return "repo:{$hash}";
   }
 
   public function newMarkupEngine($field) {
     return PhabricatorMarkupEngine::newMarkupEngine(array());
   }
 
   public function getMarkupText($field) {
     return $this->getDetail('description');
   }
 
   public function didMarkupText(
     $field,
     $output,
     PhutilMarkupEngine $engine) {
     require_celerity_resource('phabricator-remarkup-css');
     return phutil_tag(
       'div',
       array(
         'class' => 'phabricator-remarkup',
       ),
       $output);
   }
 
   public function shouldUseMarkupCache($field) {
     return true;
   }
 
 }