protected final function updateCommitData($author, $message)
 {
     $commit = $this->commit;
     $data = id(new PhabricatorRepositoryCommitData())->loadOneWhere('commitID = %d', $commit->getID());
     if (!$data) {
         $data = new PhabricatorRepositoryCommitData();
     }
     $data->setCommitID($commit->getID());
     $data->setAuthorName($author);
     $data->setCommitMessage($message);
     $repository = $this->repository;
     $detail_parser = $repository->getDetail('detail-parser', 'PhabricatorRepositoryDefaultCommitMessageDetailParser');
     if ($detail_parser) {
         PhutilSymbolLoader::loadClass($detail_parser);
         $parser_obj = newv($detail_parser, array($commit, $data));
         $parser_obj->parseCommitDetails();
     }
     $data->save();
     $revision_id = $data->getCommitDetail('differential.revisionID');
     if ($revision_id) {
         $revision = id(new DifferentialRevision())->load($revision_id);
         if ($revision) {
             queryfx($revision->establishConnection('w'), 'INSERT IGNORE INTO %T (revisionID, commitPHID) VALUES (%d, %s)', DifferentialRevision::TABLE_COMMIT, $revision->getID(), $commit->getPHID());
             if ($revision->getStatus() != DifferentialRevisionStatus::COMMITTED) {
                 $editor = new DifferentialCommentEditor($revision, $revision->getAuthorPHID(), DifferentialAction::ACTION_COMMIT);
                 $editor->save();
             }
         }
     }
 }
 protected final function updateCommitData($author, $message)
 {
     $commit = $this->commit;
     $data = id(new PhabricatorRepositoryCommitData())->loadOneWhere('commitID = %d', $commit->getID());
     if (!$data) {
         $data = new PhabricatorRepositoryCommitData();
     }
     $data->setCommitID($commit->getID());
     $data->setAuthorName($author);
     $data->setCommitMessage($message);
     $repository = $this->repository;
     $detail_parser = $repository->getDetail('detail-parser', 'PhabricatorRepositoryDefaultCommitMessageDetailParser');
     if ($detail_parser) {
         PhutilSymbolLoader::loadClass($detail_parser);
         $parser_obj = newv($detail_parser, array($commit, $data));
         $parser_obj->parseCommitDetails();
     }
     $data->save();
     $conn_w = id(new DifferentialRevision())->establishConnection('w');
     // NOTE: The `differential_commit` table has a unique ID on `commitPHID`,
     // preventing more than one revision from being associated with a commit.
     // Generally this is good and desirable, but with the advent of hash
     // tracking we may end up in a situation where we match several different
     // revisions. We just kind of ignore this and pick one, we might want to
     // revisit this and do something differently. (If we match several revisions
     // someone probably did something very silly, though.)
     $revision_id = $data->getCommitDetail('differential.revisionID');
     if (!$revision_id) {
         $hashes = $this->getCommitHashes($this->repository, $this->commit);
         if ($hashes) {
             $query = new DifferentialRevisionQuery();
             $query->withCommitHashes($hashes);
             $revisions = $query->execute();
             if (!empty($revisions)) {
                 $revision = $this->identifyBestRevision($revisions);
                 $revision_id = $revision->getID();
             }
         }
     }
     if ($revision_id) {
         $revision = id(new DifferentialRevision())->load($revision_id);
         if ($revision) {
             queryfx($conn_w, 'INSERT IGNORE INTO %T (revisionID, commitPHID) VALUES (%d, %s)', DifferentialRevision::TABLE_COMMIT, $revision->getID(), $commit->getPHID());
             if ($revision->getStatus() != DifferentialRevisionStatus::COMMITTED) {
                 $message = null;
                 $committer = $data->getCommitDetail('authorPHID');
                 if (!$committer) {
                     $committer = $revision->getAuthorPHID();
                     $message = 'Change committed by ' . $data->getAuthorName() . '.';
                 }
                 $editor = new DifferentialCommentEditor($revision, $committer, DifferentialAction::ACTION_COMMIT);
                 $editor->setMessage($message)->save();
             }
         }
     }
 }
 protected final function updateCommitData($author, $message, $committer = null)
 {
     $commit = $this->commit;
     $data = id(new PhabricatorRepositoryCommitData())->loadOneWhere('commitID = %d', $commit->getID());
     if (!$data) {
         $data = new PhabricatorRepositoryCommitData();
     }
     $data->setCommitID($commit->getID());
     $data->setAuthorName($author);
     $data->setCommitMessage($message);
     if ($committer) {
         $data->setCommitDetail('committer', $committer);
     }
     $repository = $this->repository;
     $detail_parser = $repository->getDetail('detail-parser', 'PhabricatorRepositoryDefaultCommitMessageDetailParser');
     if ($detail_parser) {
         $parser_obj = newv($detail_parser, array($commit, $data));
         $parser_obj->parseCommitDetails();
     }
     $author_phid = $this->lookupUser($commit, $data->getAuthorName(), $data->getCommitDetail('authorPHID'));
     $data->setCommitDetail('authorPHID', $author_phid);
     $committer_phid = $this->lookupUser($commit, $data->getCommitDetail('committer'), $data->getCommitDetail('committerPHID'));
     $data->setCommitDetail('committerPHID', $committer_phid);
     if ($author_phid != $commit->getAuthorPHID()) {
         $commit->setAuthorPHID($author_phid);
         $commit->save();
     }
     $conn_w = id(new DifferentialRevision())->establishConnection('w');
     // NOTE: The `differential_commit` table has a unique ID on `commitPHID`,
     // preventing more than one revision from being associated with a commit.
     // Generally this is good and desirable, but with the advent of hash
     // tracking we may end up in a situation where we match several different
     // revisions. We just kind of ignore this and pick one, we might want to
     // revisit this and do something differently. (If we match several revisions
     // someone probably did something very silly, though.)
     $revision_id = $data->getCommitDetail('differential.revisionID');
     if (!$revision_id) {
         $hashes = $this->getCommitHashes($this->repository, $this->commit);
         if ($hashes) {
             $query = new DifferentialRevisionQuery();
             $query->withCommitHashes($hashes);
             $revisions = $query->execute();
             if (!empty($revisions)) {
                 $revision = $this->identifyBestRevision($revisions);
                 $revision_id = $revision->getID();
             }
         }
     }
     if ($revision_id) {
         $revision = id(new DifferentialRevision())->load($revision_id);
         if ($revision) {
             queryfx($conn_w, 'INSERT IGNORE INTO %T (revisionID, commitPHID) VALUES (%d, %s)', DifferentialRevision::TABLE_COMMIT, $revision->getID(), $commit->getPHID());
             $committer_phid = $data->getCommitDetail('committerPHID');
             if ($committer_phid) {
                 $handle = PhabricatorObjectHandleData::loadOneHandle($committer_phid);
                 $committer_name = '@' . $handle->getName();
             } else {
                 $committer_name = $data->getCommitDetail('committer');
             }
             $author_phid = $data->getCommitDetail('authorPHID');
             if ($author_phid) {
                 $handle = PhabricatorObjectHandleData::loadOneHandle($author_phid);
                 $author_name = '@' . $handle->getName();
             } else {
                 $author_name = $data->getAuthorName();
             }
             $commit_name = $repository->formatCommitName($commit->getCommitIdentifier());
             $info = array();
             $info[] = "authored by {$author_name}";
             if ($committer_name && $committer_name != $author_name) {
                 $info[] = "committed by {$committer_name}";
             }
             $info = implode(', ', $info);
             $message = "Closed by commit {$commit_name} ({$info}).";
             $actor_phid = nonempty($committer_phid, $author_phid, $revision->getAuthorPHID());
             $status_closed = ArcanistDifferentialRevisionStatus::CLOSED;
             $should_close = $revision->getStatus() != $status_closed && $repository->shouldAutocloseCommit($commit, $data);
             if ($should_close) {
                 $diff = $this->attachToRevision($revision, $actor_phid);
                 $revision->setDateCommitted($commit->getEpoch());
                 $editor = new DifferentialCommentEditor($revision, $actor_phid, DifferentialAction::ACTION_CLOSE);
                 $editor->setIsDaemonWorkflow(true);
                 $vs_diff = $this->loadChangedByCommit($diff);
                 if ($vs_diff) {
                     $data->setCommitDetail('vsDiff', $vs_diff->getID());
                     $changed_by_commit = PhabricatorEnv::getProductionURI('/D' . $revision->getID() . '?vs=' . $vs_diff->getID() . '&id=' . $diff->getID() . '#toc');
                     $editor->setChangedByCommit($changed_by_commit);
                 }
                 $editor->setMessage($message)->save();
             }
         }
     }
     $data->save();
 }
 private function recordCommit(PhabricatorRepository $repository, $commit_identifier, $epoch, $close_immediately, array $parents)
 {
     $commit = new PhabricatorRepositoryCommit();
     $conn_w = $repository->establishConnection('w');
     // First, try to revive an existing unreachable commit (if one exists) by
     // removing the "unreachable" flag. If we succeed, we don't need to do
     // anything else: we already discovered this commit some time ago.
     queryfx($conn_w, 'UPDATE %T SET importStatus = (importStatus & ~%d)
     WHERE repositoryID = %d AND commitIdentifier = %s', $commit->getTableName(), PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE, $repository->getID(), $commit_identifier);
     if ($conn_w->getAffectedRows()) {
         $commit = $commit->loadOneWhere('repositoryID = %d AND commitIdentifier = %s', $repository->getID(), $commit_identifier);
         // After reviving a commit, schedule new daemons for it.
         $this->didDiscoverCommit($repository, $commit, $epoch);
         return;
     }
     $commit->setRepositoryID($repository->getID());
     $commit->setCommitIdentifier($commit_identifier);
     $commit->setEpoch($epoch);
     if ($close_immediately) {
         $commit->setImportStatus(PhabricatorRepositoryCommit::IMPORTED_CLOSEABLE);
     }
     $data = new PhabricatorRepositoryCommitData();
     try {
         // If this commit has parents, look up their IDs. The parent commits
         // should always exist already.
         $parent_ids = array();
         if ($parents) {
             $parent_rows = queryfx_all($conn_w, 'SELECT id, commitIdentifier FROM %T
         WHERE commitIdentifier IN (%Ls) AND repositoryID = %d', $commit->getTableName(), $parents, $repository->getID());
             $parent_map = ipull($parent_rows, 'id', 'commitIdentifier');
             foreach ($parents as $parent) {
                 if (empty($parent_map[$parent])) {
                     throw new Exception(pht('Unable to identify parent "%s"!', $parent));
                 }
                 $parent_ids[] = $parent_map[$parent];
             }
         } else {
             // Write an explicit 0 so we can distinguish between "really no
             // parents" and "data not available".
             if (!$repository->isSVN()) {
                 $parent_ids = array(0);
             }
         }
         $commit->openTransaction();
         $commit->save();
         $data->setCommitID($commit->getID());
         $data->save();
         foreach ($parent_ids as $parent_id) {
             queryfx($conn_w, 'INSERT IGNORE INTO %T (childCommitID, parentCommitID)
           VALUES (%d, %d)', PhabricatorRepository::TABLE_PARENTS, $commit->getID(), $parent_id);
         }
         $commit->saveTransaction();
         $this->didDiscoverCommit($repository, $commit, $epoch);
         if ($this->repairMode) {
             // Normally, the query should throw a duplicate key exception. If we
             // reach this in repair mode, we've actually performed a repair.
             $this->log(pht('Repaired commit "%s".', $commit_identifier));
         }
         PhutilEventEngine::dispatchEvent(new PhabricatorEvent(PhabricatorEventType::TYPE_DIFFUSION_DIDDISCOVERCOMMIT, array('repository' => $repository, 'commit' => $commit)));
     } catch (AphrontDuplicateKeyQueryException $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.
     }
 }
 private function recordCommit(PhabricatorRepository $repository, $commit_identifier, $epoch, $close_immediately, array $parents)
 {
     $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();
     $conn_w = $repository->establishConnection('w');
     try {
         // If this commit has parents, look up their IDs. The parent commits
         // should always exist already.
         $parent_ids = array();
         if ($parents) {
             $parent_rows = queryfx_all($conn_w, 'SELECT id, commitIdentifier FROM %T
         WHERE commitIdentifier IN (%Ls) AND repositoryID = %d', $commit->getTableName(), $parents, $repository->getID());
             $parent_map = ipull($parent_rows, 'id', 'commitIdentifier');
             foreach ($parents as $parent) {
                 if (empty($parent_map[$parent])) {
                     throw new Exception(pht('Unable to identify parent "%s"!', $parent));
                 }
                 $parent_ids[] = $parent_map[$parent];
             }
         } else {
             // Write an explicit 0 so we can distinguish between "really no
             // parents" and "data not available".
             if (!$repository->isSVN()) {
                 $parent_ids = array(0);
             }
         }
         $commit->openTransaction();
         $commit->save();
         $data->setCommitID($commit->getID());
         $data->save();
         foreach ($parent_ids as $parent_id) {
             queryfx($conn_w, 'INSERT IGNORE INTO %T (childCommitID, parentCommitID)
           VALUES (%d, %d)', PhabricatorRepository::TABLE_PARENTS, $commit->getID(), $parent_id);
         }
         $commit->saveTransaction();
         $this->insertTask($repository, $commit);
         queryfx($conn_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->repairMode) {
             // Normally, the query should throw a duplicate key exception. If we
             // reach this in repair mode, we've actually performed a repair.
             $this->log(pht('Repaired commit "%s".', $commit_identifier));
         }
         PhutilEventEngine::dispatchEvent(new PhabricatorEvent(PhabricatorEventType::TYPE_DIFFUSION_DIDDISCOVERCOMMIT, array('repository' => $repository, 'commit' => $commit)));
     } catch (AphrontDuplicateKeyQueryException $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.
     }
 }
 protected final function updateCommitData(DiffusionCommitRef $ref)
 {
     $commit = $this->commit;
     $author = $ref->getAuthor();
     $message = $ref->getMessage();
     $committer = $ref->getCommitter();
     $hashes = $ref->getHashes();
     $data = id(new PhabricatorRepositoryCommitData())->loadOneWhere('commitID = %d', $commit->getID());
     if (!$data) {
         $data = new PhabricatorRepositoryCommitData();
     }
     $data->setCommitID($commit->getID());
     $data->setAuthorName(id(new PhutilUTF8StringTruncator())->setMaximumBytes(255)->truncateString((string) $author));
     $data->setCommitDetail('authorName', $ref->getAuthorName());
     $data->setCommitDetail('authorEmail', $ref->getAuthorEmail());
     $data->setCommitDetail('authorPHID', $this->resolveUserPHID($commit, $author));
     $data->setCommitMessage($message);
     if (strlen($committer)) {
         $data->setCommitDetail('committer', $committer);
         $data->setCommitDetail('committerName', $ref->getCommitterName());
         $data->setCommitDetail('committerEmail', $ref->getCommitterEmail());
         $data->setCommitDetail('committerPHID', $this->resolveUserPHID($commit, $committer));
     }
     $repository = $this->repository;
     $author_phid = $data->getCommitDetail('authorPHID');
     $committer_phid = $data->getCommitDetail('committerPHID');
     $user = new PhabricatorUser();
     if ($author_phid) {
         $user = $user->loadOneWhere('phid = %s', $author_phid);
     }
     $differential_app = 'PhabricatorDifferentialApplication';
     $revision_id = null;
     $low_level_query = null;
     if (PhabricatorApplication::isClassInstalled($differential_app)) {
         $low_level_query = id(new DiffusionLowLevelCommitFieldsQuery())->setRepository($repository)->withCommitRef($ref);
         $field_values = $low_level_query->execute();
         $revision_id = idx($field_values, 'revisionID');
         if (!empty($field_values['reviewedByPHIDs'])) {
             $data->setCommitDetail('reviewerPHID', reset($field_values['reviewedByPHIDs']));
         }
         $data->setCommitDetail('differential.revisionID', $revision_id);
     }
     if ($author_phid != $commit->getAuthorPHID()) {
         $commit->setAuthorPHID($author_phid);
     }
     $commit->setSummary($data->getSummary());
     $commit->save();
     // Figure out if we're going to try to "autoclose" related objects (e.g.,
     // close linked tasks and related revisions) and, if not, record why we
     // aren't. Autoclose can be disabled for various reasons at the repository
     // or commit levels.
     $force_autoclose = idx($this->getTaskData(), 'forceAutoclose', false);
     if ($force_autoclose) {
         $autoclose_reason = PhabricatorRepository::BECAUSE_AUTOCLOSE_FORCED;
     } else {
         $autoclose_reason = $repository->shouldSkipAutocloseCommit($commit);
     }
     $data->setCommitDetail('autocloseReason', $autoclose_reason);
     $should_autoclose = $force_autoclose || $repository->shouldAutocloseCommit($commit);
     // When updating related objects, we'll act under an omnipotent user to
     // ensure we can see them, but take actions as either the committer or
     // author (if we recognize their accounts) or the Diffusion application
     // (if we do not).
     $actor = PhabricatorUser::getOmnipotentUser();
     $acting_as_phid = nonempty($committer_phid, $author_phid, id(new PhabricatorDiffusionApplication())->getPHID());
     $conn_w = id(new DifferentialRevision())->establishConnection('w');
     // NOTE: The `differential_commit` table has a unique ID on `commitPHID`,
     // preventing more than one revision from being associated with a commit.
     // Generally this is good and desirable, but with the advent of hash
     // tracking we may end up in a situation where we match several different
     // revisions. We just kind of ignore this and pick one, we might want to
     // revisit this and do something differently. (If we match several revisions
     // someone probably did something very silly, though.)
     $revision = null;
     if ($revision_id) {
         $revision_query = id(new DifferentialRevisionQuery())->withIDs(array($revision_id))->setViewer($actor)->needReviewerStatus(true)->needActiveDiffs(true);
         $revision = $revision_query->executeOne();
         if ($revision) {
             if (!$data->getCommitDetail('precommitRevisionStatus')) {
                 $data->setCommitDetail('precommitRevisionStatus', $revision->getStatus());
             }
             $commit_drev = DiffusionCommitHasRevisionEdgeType::EDGECONST;
             id(new PhabricatorEdgeEditor())->addEdge($commit->getPHID(), $commit_drev, $revision->getPHID())->save();
             queryfx($conn_w, 'INSERT IGNORE INTO %T (revisionID, commitPHID) VALUES (%d, %s)', DifferentialRevision::TABLE_COMMIT, $revision->getID(), $commit->getPHID());
             $status_closed = ArcanistDifferentialRevisionStatus::CLOSED;
             $should_close = $revision->getStatus() != $status_closed && $should_autoclose;
             if ($should_close) {
                 $commit_close_xaction = id(new DifferentialTransaction())->setTransactionType(DifferentialTransaction::TYPE_ACTION)->setNewValue(DifferentialAction::ACTION_CLOSE)->setMetadataValue('isCommitClose', true);
                 $commit_close_xaction->setMetadataValue('commitPHID', $commit->getPHID());
                 $commit_close_xaction->setMetadataValue('committerPHID', $committer_phid);
                 $commit_close_xaction->setMetadataValue('committerName', $data->getCommitDetail('committer'));
                 $commit_close_xaction->setMetadataValue('authorPHID', $author_phid);
                 $commit_close_xaction->setMetadataValue('authorName', $data->getAuthorName());
                 if ($low_level_query) {
                     $commit_close_xaction->setMetadataValue('revisionMatchData', $low_level_query->getRevisionMatchData());
                     $data->setCommitDetail('revisionMatchData', $low_level_query->getRevisionMatchData());
                 }
                 $diff = $this->generateFinalDiff($revision, $acting_as_phid);
                 $vs_diff = $this->loadChangedByCommit($revision, $diff);
                 $changed_uri = null;
                 if ($vs_diff) {
                     $data->setCommitDetail('vsDiff', $vs_diff->getID());
                     $changed_uri = PhabricatorEnv::getProductionURI('/D' . $revision->getID() . '?vs=' . $vs_diff->getID() . '&id=' . $diff->getID() . '#toc');
                 }
                 $xactions = array();
                 $xactions[] = id(new DifferentialTransaction())->setTransactionType(DifferentialTransaction::TYPE_UPDATE)->setIgnoreOnNoEffect(true)->setNewValue($diff->getPHID())->setMetadataValue('isCommitUpdate', true);
                 $xactions[] = $commit_close_xaction;
                 $content_source = PhabricatorContentSource::newForSource(PhabricatorContentSource::SOURCE_DAEMON, array());
                 $editor = id(new DifferentialTransactionEditor())->setActor($actor)->setActingAsPHID($acting_as_phid)->setContinueOnMissingFields(true)->setContentSource($content_source)->setChangedPriorToCommitURI($changed_uri)->setIsCloseByCommit(true);
                 try {
                     $editor->applyTransactions($revision, $xactions);
                 } catch (PhabricatorApplicationTransactionNoEffectException $ex) {
                     // NOTE: We've marked transactions other than the CLOSE transaction
                     // as ignored when they don't have an effect, so this means that we
                     // lost a race to close the revision. That's perfectly fine, we can
                     // just continue normally.
                 }
             }
         }
     }
     if ($should_autoclose) {
         $this->closeTasks($actor, $acting_as_phid, $repository, $commit, $message);
     }
     $data->save();
     $commit->writeImportStatusFlag(PhabricatorRepositoryCommit::IMPORTED_MESSAGE);
 }
 private function lookupSvnCommits(PhabricatorRepository $repository, array $commits)
 {
     if (!$commits) {
         return array();
     }
     $commit_table = new PhabricatorRepositoryCommit();
     $commit_data = queryfx_all($commit_table->establishConnection('w'), 'SELECT id, commitIdentifier FROM %T
     WHERE repositoryID = %d AND commitIdentifier in (%Ls)', $commit_table->getTableName(), $repository->getID(), $commits);
     $commit_map = ipull($commit_data, 'id', 'commitIdentifier');
     $need = array();
     foreach ($commits as $commit) {
         if (empty($commit_map[$commit])) {
             $need[] = $commit;
         }
     }
     // If we are parsing a Subversion repository and have been configured to
     // import only some subdirectory of it, we may find commits which reference
     // other foreign commits outside of the directory (for instance, because of
     // a move or copy). Rather than trying to execute full parses on them, just
     // create stub commits and identify the stubs as foreign commits.
     if ($need) {
         $subpath = $repository->getDetail('svn-subpath');
         if (!$subpath) {
             $commits = implode(', ', $need);
             throw new Exception("Missing commits ({$need}) in a SVN repository which is not " . "configured for subdirectory-only parsing!");
         }
         foreach ($need as $foreign_commit) {
             $commit = new PhabricatorRepositoryCommit();
             $commit->setRepositoryID($repository->getID());
             $commit->setCommitIdentifier($foreign_commit);
             $commit->setEpoch(0);
             // Mark this commit as imported so it doesn't prevent the repository
             // from transitioning into the "Imported" state.
             $commit->setImportStatus(PhabricatorRepositoryCommit::IMPORTED_ALL);
             $commit->save();
             $data = new PhabricatorRepositoryCommitData();
             $data->setCommitID($commit->getID());
             $data->setAuthorName('');
             $data->setCommitMessage('');
             $data->setCommitDetails(array('foreign-svn-stub' => true, 'svn-subpath' => $subpath));
             $data->save();
             $commit_map[$foreign_commit] = $commit->getID();
         }
     }
     return $commit_map;
 }
 protected final function updateCommitData($author, $message, $committer = null)
 {
     $commit = $this->commit;
     $data = id(new PhabricatorRepositoryCommitData())->loadOneWhere('commitID = %d', $commit->getID());
     if (!$data) {
         $data = new PhabricatorRepositoryCommitData();
     }
     $data->setCommitID($commit->getID());
     $data->setAuthorName($author);
     $data->setCommitMessage($message);
     if ($committer) {
         $data->setCommitDetail('committer', $committer);
     }
     $repository = $this->repository;
     $detail_parser = $repository->getDetail('detail-parser', 'PhabricatorRepositoryDefaultCommitMessageDetailParser');
     if ($detail_parser) {
         $parser_obj = newv($detail_parser, array($commit, $data));
         $parser_obj->parseCommitDetails();
     }
     $author_phid = $this->lookupUser($commit, $data->getAuthorName(), $data->getCommitDetail('authorPHID'));
     $data->setCommitDetail('authorPHID', $author_phid);
     $committer_phid = $this->lookupUser($commit, $data->getCommitDetail('committer'), $data->getCommitDetail('committerPHID'));
     $data->setCommitDetail('committerPHID', $committer_phid);
     if ($author_phid != $commit->getAuthorPHID()) {
         $commit->setAuthorPHID($author_phid);
         $commit->save();
     }
     $conn_w = id(new DifferentialRevision())->establishConnection('w');
     // NOTE: The `differential_commit` table has a unique ID on `commitPHID`,
     // preventing more than one revision from being associated with a commit.
     // Generally this is good and desirable, but with the advent of hash
     // tracking we may end up in a situation where we match several different
     // revisions. We just kind of ignore this and pick one, we might want to
     // revisit this and do something differently. (If we match several revisions
     // someone probably did something very silly, though.)
     $revision = null;
     $should_autoclose = $repository->shouldAutocloseCommit($commit, $data);
     $revision_id = $data->getCommitDetail('differential.revisionID');
     if (!$revision_id) {
         $hashes = $this->getCommitHashes($this->repository, $this->commit);
         if ($hashes) {
             $query = new DifferentialRevisionQuery();
             $query->withCommitHashes($hashes);
             $revisions = $query->execute();
             if (!empty($revisions)) {
                 $revision = $this->identifyBestRevision($revisions);
                 $revision_id = $revision->getID();
             }
         }
     }
     if ($revision_id) {
         $lock = PhabricatorGlobalLock::newLock(get_class($this) . ':' . $revision_id);
         $lock->lock(5 * 60);
         $revision = id(new DifferentialRevision())->load($revision_id);
         if ($revision) {
             $revision->loadRelationships();
             queryfx($conn_w, 'INSERT IGNORE INTO %T (revisionID, commitPHID) VALUES (%d, %s)', DifferentialRevision::TABLE_COMMIT, $revision->getID(), $commit->getPHID());
             $status_closed = ArcanistDifferentialRevisionStatus::CLOSED;
             $should_close = $revision->getStatus() != $status_closed && $should_autoclose;
             if ($should_close) {
                 $actor_phid = nonempty($committer_phid, $author_phid, $revision->getAuthorPHID());
                 $diff = $this->attachToRevision($revision, $actor_phid);
                 $revision->setDateCommitted($commit->getEpoch());
                 $editor = new DifferentialCommentEditor($revision, $actor_phid, DifferentialAction::ACTION_CLOSE);
                 $editor->setIsDaemonWorkflow(true);
                 $vs_diff = $this->loadChangedByCommit($diff);
                 if ($vs_diff) {
                     $data->setCommitDetail('vsDiff', $vs_diff->getID());
                     $changed_by_commit = PhabricatorEnv::getProductionURI('/D' . $revision->getID() . '?vs=' . $vs_diff->getID() . '&id=' . $diff->getID() . '#toc');
                     $editor->setChangedByCommit($changed_by_commit);
                 }
                 $commit_name = $repository->formatCommitName($commit->getCommitIdentifier());
                 $committer_name = $this->loadUserName($committer_phid, $data->getCommitDetail('committer'));
                 $author_name = $this->loadUserName($author_phid, $data->getAuthorName());
                 $info = array();
                 $info[] = "authored by {$author_name}";
                 if ($committer_name && $committer_name != $author_name) {
                     $info[] = "committed by {$committer_name}";
                 }
                 $info = implode(', ', $info);
                 $editor->setMessage("Closed by commit {$commit_name} ({$info}).")->save();
             }
         }
         $lock->unlock();
     }
     if ($should_autoclose && $author_phid) {
         $user = id(new PhabricatorUser())->loadOneWhere('phid = %s', $author_phid);
         $call = new ConduitCall('differential.parsecommitmessage', array('corpus' => $message, 'partial' => true));
         $call->setUser($user);
         $result = $call->execute();
         $field_values = $result['fields'];
         $fields = DifferentialFieldSelector::newSelector()->getFieldSpecifications();
         foreach ($fields as $key => $field) {
             if (!$field->shouldAppearOnCommitMessage()) {
                 continue;
             }
             $field->setUser($user);
             $value = idx($field_values, $field->getCommitMessageKey());
             $field->setValueFromParsedCommitMessage($value);
             if ($revision) {
                 $field->setRevision($revision);
             }
             $field->didParseCommit($repository, $commit, $data);
         }
     }
     $data->save();
 }
 private function updateCommit(PhabricatorRepository $repository, $commit_identifier, $branch)
 {
     $commit = id(new PhabricatorRepositoryCommit())->loadOneWhere('repositoryID = %s 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));
 }