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();
 }
 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();
 }