public static function renderLastModifiedColumns(PhabricatorRepository $repository, array $handles, PhabricatorRepositoryCommit $commit = null, PhabricatorRepositoryCommitData $data = null)
 {
     if ($commit) {
         $epoch = $commit->getEpoch();
         $modified = DiffusionView::linkCommit($repository, $commit->getCommitIdentifier());
         $date = date('M j, Y', $epoch);
         $time = date('g:i A', $epoch);
     } else {
         $modified = '';
         $date = '';
         $time = '';
     }
     if ($data) {
         $author_phid = $data->getCommitDetail('authorPHID');
         if ($author_phid && isset($handles[$author_phid])) {
             $author = $handles[$author_phid]->renderLink();
         } else {
             $author = phutil_escape_html($data->getAuthorName());
         }
         $details = AphrontTableView::renderSingleDisplayLine(phutil_escape_html($data->getSummary()));
     } else {
         $author = '';
         $details = '';
     }
     return array('commit' => $modified, 'date' => $date, 'time' => $time, 'author' => $author, 'details' => $details);
 }
 public function loadObjects()
 {
     $types = array();
     foreach ($this->phids as $phid) {
         $type = phid_get_type($phid);
         $types[$type][] = $phid;
     }
     $objects = array();
     foreach ($types as $type => $phids) {
         switch ($type) {
             case PhabricatorPHIDConstants::PHID_TYPE_USER:
                 $user_dao = new PhabricatorUser();
                 $users = $user_dao->loadAllWhere('phid in (%Ls)', $phids);
                 foreach ($users as $user) {
                     $objects[$user->getPHID()] = $user;
                 }
                 break;
             case PhabricatorPHIDConstants::PHID_TYPE_CMIT:
                 $commit_dao = new PhabricatorRepositoryCommit();
                 $commits = $commit_dao->loadAllWhere('phid IN (%Ls)', $phids);
                 $commit_data = array();
                 if ($commits) {
                     $data_dao = new PhabricatorRepositoryCommitData();
                     $commit_data = $data_dao->loadAllWhere('commitID IN (%Ld)', mpull($commits, 'getID'));
                     $commit_data = mpull($commit_data, null, 'getCommitID');
                 }
                 foreach ($commits as $commit) {
                     $data = idx($commit_data, $commit->getID());
                     if ($data) {
                         $commit->attachCommitData($data);
                         $objects[$commit->getPHID()] = $commit;
                     } else {
                         // If we couldn't load the commit data, just act as though we
                         // couldn't load the object at all so we don't load half an object.
                     }
                 }
                 break;
             case PhabricatorPHIDConstants::PHID_TYPE_TASK:
                 $task_dao = new ManiphestTask();
                 $tasks = $task_dao->loadAllWhere('phid IN (%Ls)', $phids);
                 foreach ($tasks as $task) {
                     $objects[$task->getPHID()] = $task;
                 }
                 break;
             case PhabricatorPHIDConstants::PHID_TYPE_DREV:
                 $revision_dao = new DifferentialRevision();
                 $revisions = $revision_dao->loadAllWhere('phid IN (%Ls)', $phids);
                 foreach ($revisions as $revision) {
                     $objects[$revision->getPHID()] = $revision;
                 }
                 break;
         }
     }
     return $objects;
 }
 public function didParseCommit(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit, PhabricatorRepositoryCommitData $data)
 {
     $user = id(new PhabricatorUser())->loadOneWhere('phid = %s', $data->getCommitDetail('authorPHID'));
     if (!$user) {
         return;
     }
     $prefixes = array('resolves' => ManiphestTaskStatus::STATUS_CLOSED_RESOLVED, 'fixes' => ManiphestTaskStatus::STATUS_CLOSED_RESOLVED, 'wontfix' => ManiphestTaskStatus::STATUS_CLOSED_WONTFIX, 'wontfixes' => ManiphestTaskStatus::STATUS_CLOSED_WONTFIX, 'spite' => ManiphestTaskStatus::STATUS_CLOSED_SPITE, 'spites' => ManiphestTaskStatus::STATUS_CLOSED_SPITE, 'invalidate' => ManiphestTaskStatus::STATUS_CLOSED_INVALID, 'invaldiates' => ManiphestTaskStatus::STATUS_CLOSED_INVALID, 'close' => ManiphestTaskStatus::STATUS_CLOSED_RESOLVED, 'closes' => ManiphestTaskStatus::STATUS_CLOSED_RESOLVED, 'ref' => null, 'refs' => null, 'references' => null, 'cf.' => null);
     $suffixes = array('as resolved' => ManiphestTaskStatus::STATUS_CLOSED_RESOLVED, 'as fixed' => ManiphestTaskStatus::STATUS_CLOSED_RESOLVED, 'as wontfix' => ManiphestTaskStatus::STATUS_CLOSED_WONTFIX, 'as spite' => ManiphestTaskStatus::STATUS_CLOSED_SPITE, 'out of spite' => ManiphestTaskStatus::STATUS_CLOSED_SPITE, 'as invalid' => ManiphestTaskStatus::STATUS_CLOSED_INVALID, '' => null);
     $prefix_regex = array();
     foreach ($prefixes as $prefix => $resolution) {
         $prefix_regex[] = preg_quote($prefix, '/');
     }
     $prefix_regex = implode('|', $prefix_regex);
     $suffix_regex = array();
     foreach ($suffixes as $suffix => $resolution) {
         $suffix_regex[] = preg_quote($suffix, '/');
     }
     $suffix_regex = implode('|', $suffix_regex);
     $matches = null;
     $ok = preg_match_all("/({$prefix_regex})\\s+T(\\d+)\\s*({$suffix_regex})/i", $this->renderValueForCommitMessage($is_edit = false), $matches, PREG_SET_ORDER);
     if (!$ok) {
         return;
     }
     foreach ($matches as $set) {
         $prefix = strtolower($set[1]);
         $task_id = (int) $set[2];
         $suffix = strtolower($set[3]);
         $status = idx($suffixes, $suffix);
         if (!$status) {
             $status = idx($prefixes, $prefix);
         }
         $tasks = id(new ManiphestTaskQuery())->withTaskIDs(array($task_id))->execute();
         $task = idx($tasks, $task_id);
         if (!$task) {
             // Task doesn't exist, or the user can't see it.
             continue;
         }
         id(new PhabricatorEdgeEditor())->setUser($user)->addEdge($task->getPHID(), PhabricatorEdgeConfig::TYPE_TASK_HAS_COMMIT, $commit->getPHID())->save();
         if (!$status) {
             // Text like "Ref T123", don't change the task status.
             continue;
         }
         if ($task->getStatus() != ManiphestTaskStatus::STATUS_OPEN) {
             // Task is already closed.
             continue;
         }
         $commit_name = $repository->formatCommitName($commit->getCommitIdentifier());
         $call = new ConduitCall('maniphest.update', array('id' => $task->getID(), 'status' => $status, 'comments' => "Closed by commit {$commit_name}."));
         $call->setUser($user);
         $call->execute();
     }
 }
 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)
 {
     $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();
             }
         }
     }
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $query = $request->getStr('q');
     $repo_id = $request->getInt('repo');
     $since = $request->getInt('since');
     $limit = $request->getInt('limit');
     $now = time();
     $data = array();
     // Dummy instances used for getting connections, table names, etc.
     $pr_commit = new PhabricatorRepositoryCommit();
     $pr_commit_data = new PhabricatorRepositoryCommitData();
     $conn = $pr_commit->establishConnection('r');
     $rows = queryfx_all($conn, 'SELECT
     rc.phid as commitPHID,
     rc.authorPHID,
     rcd.authorName,
     SUBSTRING(rcd.commitMessage, 1, 100) AS shortMessage,
     rc.commitIdentifier,
     rc.epoch
     FROM %T rc
     INNER JOIN %T rcd ON rcd.commitID = rc.id
     WHERE repositoryID = %d
     AND rc.epoch >= %d
     AND (
       rcd.commitMessage LIKE %~
       OR
       rc.commitIdentifier LIKE %~
     )
     ORDER BY rc.epoch DESC
     LIMIT %d', $pr_commit->getTableName(), $pr_commit_data->getTableName(), $repo_id, $since, $query, $query, $limit);
     foreach ($rows as $row) {
         $full_commit_id = $row['commitIdentifier'];
         $short_commit_id = substr($full_commit_id, 0, 12);
         $first_line = $this->getFirstLine($row['shortMessage']);
         $data[] = array($full_commit_id, $short_commit_id, $row['authorName'], phutil_format_relative_time($now - $row['epoch']), $first_line);
     }
     return id(new AphrontAjaxResponse())->setContent($data);
 }
 public function testSummarizeCommits()
 {
     // Cyrillic "zhe".
     $zhe = "ะถ";
     // Symbol "Snowman".
     $snowman = "โ˜ƒ";
     // Emoji "boar".
     $boar = "๐Ÿ—";
     // Proper unicode truncation is tested elsewhere, this is just making
     // sure column length handling is sane.
     $map = array('' => 0, 'a' => 1, str_repeat('a', 81) => 82, str_repeat('a', 255) => 82, str_repeat('aa ', 30) => 80, str_repeat($zhe, 300) => 161, str_repeat($snowman, 300) => 240, str_repeat($boar, 300) => 255);
     foreach ($map as $input => $expect) {
         $actual = PhabricatorRepositoryCommitData::summarizeCommitMessage($input);
         $this->assertEqual($expect, strlen($actual));
     }
 }
 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();
 }
Example #9
0
 public function loadCommitData()
 {
     if (empty($this->repositoryCommitData)) {
         $commit = $this->loadCommit();
         $data = id(new PhabricatorRepositoryCommitData())->loadOneWhere('commitID = %d', $commit->getID());
         if (!$data) {
             $data = new PhabricatorRepositoryCommitData();
             $data->setCommitMessage(pht('(This commit has not been fully parsed yet.)'));
         }
         $this->repositoryCommitData = $data;
     }
     return $this->repositoryCommitData;
 }
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
echo "Updating old commit authors...\n";
$table = new PhabricatorRepositoryCommit();
$conn = $table->establishConnection('w');
$data = new PhabricatorRepositoryCommitData();
$commits = queryfx_all($conn, 'SELECT c.id id, c.authorPHID authorPHID, d.commitDetails details
    FROM %T c JOIN %T d ON d.commitID = c.id
    WHERE c.authorPHID IS NULL', $table->getTableName(), $data->getTableName());
foreach ($commits as $commit) {
    $id = $commit['id'];
    $details = json_decode($commit['details'], true);
    $author_phid = idx($details, 'authorPHID');
    if ($author_phid) {
        queryfx($conn, 'UPDATE %T SET authorPHID = %s WHERE id = %d', $table->getTableName(), $author_phid, $id);
        echo "#{$id}\n";
    }
}
echo "Done.\n";
echo "Updating old commit mailKeys...\n";
$table = new PhabricatorRepositoryCommit();
 public static function newFromConduit(array $dicts)
 {
     $results = array();
     foreach ($dicts as $dict) {
         $commit = PhabricatorRepositoryCommit::newFromDictionary($dict['commit']);
         $commit_data = PhabricatorRepositoryCommitData::newFromDictionary($dict['commitData']);
         $results[] = id(new DiffusionPathChange())->setPath($dict['path'])->setCommitIdentifier($dict['commitIdentifier'])->setCommit($commit)->setCommitData($commit_data)->setFileType($dict['fileType'])->setChangeType($dict['changeType'])->setTargetPath($dict['targetPath'])->setTargetCommitIdentifier($dict['targetCommitIdentifier'])->setAwayPaths($dict['awayPaths']);
     }
     return $results;
 }
 private function getCommitProperties(PhabricatorRepositoryCommit $commit, PhabricatorRepositoryCommitData $data, array $parents)
 {
     assert_instances_of($parents, 'PhabricatorRepositoryCommit');
     $user = $this->getRequest()->getUser();
     $task_phids = PhabricatorEdgeQuery::loadDestinationPHIDs($commit->getPHID(), PhabricatorEdgeConfig::TYPE_COMMIT_HAS_TASK);
     $phids = $task_phids;
     if ($data->getCommitDetail('authorPHID')) {
         $phids[] = $data->getCommitDetail('authorPHID');
     }
     if ($data->getCommitDetail('reviewerPHID')) {
         $phids[] = $data->getCommitDetail('reviewerPHID');
     }
     if ($data->getCommitDetail('differential.revisionPHID')) {
         $phids[] = $data->getCommitDetail('differential.revisionPHID');
     }
     if ($parents) {
         foreach ($parents as $parent) {
             $phids[] = $parent->getPHID();
         }
     }
     $handles = array();
     if ($phids) {
         $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
     }
     $props = array();
     if ($commit->getAuditStatus()) {
         $status = PhabricatorAuditCommitStatusConstants::getStatusName($commit->getAuditStatus());
         $props['Status'] = phutil_render_tag('strong', array(), phutil_escape_html($status));
     }
     $props['Committed'] = phabricator_datetime($commit->getEpoch(), $user);
     $author_phid = $data->getCommitDetail('authorPHID');
     if ($data->getCommitDetail('authorPHID')) {
         $props['Author'] = $handles[$author_phid]->renderLink();
     } else {
         $props['Author'] = phutil_escape_html($data->getAuthorName());
     }
     $reviewer_phid = $data->getCommitDetail('reviewerPHID');
     $reviewer_name = $data->getCommitDetail('reviewerName');
     if ($reviewer_phid) {
         $props['Reviewer'] = $handles[$reviewer_phid]->renderLink();
     } else {
         if ($reviewer_name) {
             $props['Reviewer'] = phutil_escape_html($reviewer_name);
         }
     }
     $revision_phid = $data->getCommitDetail('differential.revisionPHID');
     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'] = implode(' · ', $parent_links);
     }
     $request = $this->getDiffusionRequest();
     $contains = DiffusionContainsQuery::newFromDiffusionRequest($request);
     $branches = $contains->loadContainingBranches();
     if ($branches) {
         // TODO: Separate these into 'tracked' and other; link tracked branches.
         $branches = implode(', ', array_keys($branches));
         $branches = phutil_escape_html($branches);
         $props['Branches'] = $branches;
     }
     if ($task_phids) {
         $task_list = array();
         foreach ($task_phids as $phid) {
             $task_list[] = $handles[$phid]->renderLink();
         }
         $task_list = implode('<br />', $task_list);
         $props['Tasks'] = $task_list;
     }
     return $props;
 }
 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.
     }
 }
Example #14
0
 public function getSummary()
 {
     return PhabricatorRepositoryCommitData::summarizeCommitMessage($this->getMessage());
 }
 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();
 }
 public function didParseCommit(PhabricatorRepository $repo, PhabricatorRepositoryCommit $commit, PhabricatorRepositoryCommitData $data)
 {
     // NOTE: This is currently dead code. See T2222.
     $releeph_requests = $this->loadReleephRequests();
     if (!$releeph_requests) {
         return;
     }
     $releeph_branch = head($releeph_requests)->getBranch();
     if (!$this->isCommitOnBranch($repo, $commit, $releeph_branch)) {
         return;
     }
     foreach ($releeph_requests as $releeph_request) {
         if ($this->releephAction === self::ACTION_PICKS) {
             $action = 'pick';
         } else {
             $action = 'revert';
         }
         $actor_phid = coalesce($data->getCommitDetail('committerPHID'), $data->getCommitDetail('authorPHID'));
         $actor = id(new PhabricatorUser())->loadOneWhere('phid = %s', $actor_phid);
         $xactions = array();
         $xactions[] = id(new ReleephRequestTransaction())->setTransactionType(ReleephRequestTransaction::TYPE_DISCOVERY)->setMetadataValue('action', $action)->setMetadataValue('authorPHID', $data->getCommitDetail('authorPHID'))->setMetadataValue('committerPHID', $data->getCommitDetail('committerPHID'))->setNewValue($commit->getPHID());
         $editor = id(new ReleephRequestTransactionalEditor())->setActor($actor)->setContinueOnNoEffect(true)->setContentSource(PhabricatorContentSource::newForSource(PhabricatorContentSource::SOURCE_UNKNOWN, array()));
         $editor->applyTransactions($releeph_request, $xactions);
     }
 }
 private function buildSubheaderView(PhabricatorRepositoryCommit $commit, PhabricatorRepositoryCommitData $data)
 {
     $viewer = $this->getViewer();
     $drequest = $this->getDiffusionRequest();
     $repository = $drequest->getRepository();
     if ($repository->isSVN()) {
         return null;
     }
     $author_phid = $data->getCommitDetail('authorPHID');
     $author_name = $data->getAuthorName();
     $author_epoch = $data->getCommitDetail('authorEpoch');
     $date = null;
     if ($author_epoch !== null) {
         $date = phabricator_datetime($author_epoch, $viewer);
     }
     if ($author_phid) {
         $handles = $viewer->loadHandles(array($author_phid));
         $image_uri = $handles[$author_phid]->getImageURI();
         $image_href = $handles[$author_phid]->getURI();
         $author = $handles[$author_phid]->renderLink();
     } else {
         if (strlen($author_name)) {
             $author = $author_name;
             $image_uri = null;
             $image_href = null;
         } else {
             return null;
         }
     }
     $author = phutil_tag('strong', array(), $author);
     if ($date) {
         $content = pht('Authored by %s on %s.', $author, $date);
     } else {
         $content = pht('Authored by %s.', $author);
     }
     return id(new PHUIHeadThingView())->setImage($image_uri)->setImageHref($image_href)->setContent($content);
 }
 public static function newFromDictionary(array $dict)
 {
     $path = id(new DiffusionRepositoryPath())->setFullPath($dict['fullPath'])->setPath($dict['path'])->setHash($dict['hash'])->setFileType($dict['fileType'])->setFileSize($dict['fileSize'])->setExternalURI($dict['externalURI']);
     if ($dict['lastModifiedCommit']) {
         $last_modified_commit = PhabricatorRepositoryCommit::newFromDictionary($dict['lastModifiedCommit']);
         $path->setLastModifiedCommit($last_modified_commit);
     }
     if ($dict['lastCommitData']) {
         $last_commit_data = PhabricatorRepositoryCommitData::newFromDictionary($dict['lastCommitData']);
         $path->setLastCommitData($last_commit_data);
     }
     return $path;
 }
<?php

echo "Updating old commit authors...\n";
$table = new PhabricatorRepositoryCommit();
$table->openTransaction();
$conn = $table->establishConnection('w');
$data = new PhabricatorRepositoryCommitData();
$commits = queryfx_all($conn, 'SELECT c.id id, c.authorPHID authorPHID, d.commitDetails details
    FROM %T c JOIN %T d ON d.commitID = c.id
    WHERE c.authorPHID IS NULL
    FOR UPDATE', $table->getTableName(), $data->getTableName());
foreach ($commits as $commit) {
    $id = $commit['id'];
    $details = json_decode($commit['details'], true);
    $author_phid = idx($details, 'authorPHID');
    if ($author_phid) {
        queryfx($conn, 'UPDATE %T SET authorPHID = %s WHERE id = %d', $table->getTableName(), $author_phid, $id);
        echo "#{$id}\n";
    }
}
$table->saveTransaction();
echo "Done.\n";
echo "Updating old commit mailKeys...\n";
$table->openTransaction();
$commits = queryfx_all($conn, 'SELECT id FROM %T WHERE mailKey = %s FOR UPDATE', $table->getTableName(), '');
foreach ($commits as $commit) {
    $id = $commit['id'];
    queryfx($conn, 'UPDATE %T SET mailKey = %s WHERE id = %d', $table->getTableName(), Filesystem::readRandomCharacters(20), $id);
    echo "#{$id}\n";
}
$table->saveTransaction();
 private function getCommitProperties(PhabricatorRepositoryCommit $commit, PhabricatorRepositoryCommitData $data, array $parents)
 {
     assert_instances_of($parents, 'PhabricatorRepositoryCommit');
     $user = $this->getRequest()->getUser();
     $commit_phid = $commit->getPHID();
     $edges = id(new PhabricatorEdgeQuery())->withSourcePHIDs(array($commit_phid))->withEdgeTypes(array(PhabricatorEdgeConfig::TYPE_COMMIT_HAS_TASK, PhabricatorEdgeConfig::TYPE_COMMIT_HAS_PROJECT))->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]);
     $phids = array_merge($task_phids, $proj_phids);
     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 ($data->getCommitDetail('differential.revisionPHID')) {
         $phids[] = $data->getCommitDetail('differential.revisionPHID');
     }
     if ($parents) {
         foreach ($parents as $parent) {
             $phids[] = $parent->getPHID();
         }
     }
     $handles = array();
     if ($phids) {
         $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
     }
     $props = array();
     if ($commit->getAuditStatus()) {
         $status = PhabricatorAuditCommitStatusConstants::getStatusName($commit->getAuditStatus());
         $props['Status'] = phutil_render_tag('strong', array(), phutil_escape_html($status));
     }
     $props['Committed'] = phabricator_datetime($commit->getEpoch(), $user);
     $author_phid = $data->getCommitDetail('authorPHID');
     if ($data->getCommitDetail('authorPHID')) {
         $props['Author'] = $handles[$author_phid]->renderLink();
     } else {
         $props['Author'] = phutil_escape_html($data->getAuthorName());
     }
     $reviewer_phid = $data->getCommitDetail('reviewerPHID');
     $reviewer_name = $data->getCommitDetail('reviewerName');
     if ($reviewer_phid) {
         $props['Reviewer'] = $handles[$reviewer_phid]->renderLink();
     } else {
         if ($reviewer_name) {
             $props['Reviewer'] = phutil_escape_html($reviewer_name);
         }
     }
     $committer = $data->getCommitDetail('committer');
     if ($committer) {
         $committer_phid = $data->getCommitDetail('committerPHID');
         if ($data->getCommitDetail('committerPHID')) {
             $props['Committer'] = $handles[$committer_phid]->renderLink();
         } else {
             $props['Committer'] = phutil_escape_html($committer);
         }
     }
     $revision_phid = $data->getCommitDetail('differential.revisionPHID');
     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'] = implode(' &middot; ', $parent_links);
     }
     $request = $this->getDiffusionRequest();
     $props['Branches'] = '<span id="commit-branches">Unknown</span>';
     $props['Tags'] = '<span id="commit-tags">Unknown</span>';
     $callsign = $request->getRepository()->getCallsign();
     $root = '/diffusion/' . $callsign . '/commit/' . $commit->getCommitIdentifier();
     Javelin::initBehavior('diffusion-commit-branches', array($root . '/branches/' => 'commit-branches', $root . '/tags/' => 'commit-tags'));
     $refs = $this->buildRefs($request);
     if ($refs) {
         $props['References'] = $refs;
     }
     if ($task_phids) {
         $task_list = array();
         foreach ($task_phids as $phid) {
             $task_list[] = $handles[$phid]->renderLink();
         }
         $task_list = implode('<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 = implode('<br />', $proj_list);
         $props['Projects'] = $proj_list;
     }
     return $props;
 }
 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));
 }
 private function renderPropertyTable(PhabricatorRepositoryCommit $commit, PhabricatorRepositoryCommitData $data)
 {
     $phids = array();
     if ($data->getCommitDetail('authorPHID')) {
         $phids[] = $data->getCommitDetail('authorPHID');
     }
     if ($data->getCommitDetail('reviewerPHID')) {
         $phids[] = $data->getCommitDetail('reviewerPHID');
     }
     if ($data->getCommitDetail('differential.revisionPHID')) {
         $phids[] = $data->getCommitDetail('differential.revisionPHID');
     }
     $handles = array();
     if ($phids) {
         $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
     }
     $props = array();
     $author_phid = $data->getCommitDetail('authorPHID');
     if ($data->getCommitDetail('authorPHID')) {
         $props['Author'] = $handles[$author_phid]->renderLink();
     } else {
         $props['Author'] = phutil_escape_html($data->getAuthorName());
     }
     $reviewer_phid = $data->getCommitDetail('reviewerPHID');
     $reviewer_name = $data->getCommitDetail('reviewerName');
     if ($reviewer_phid) {
         $props['Reviewer'] = $handles[$reviewer_phid]->renderLink();
     } else {
         if ($reviewer_name) {
             $props['Reviewer'] = phutil_escape_html($reviewer_name);
         }
     }
     $revision_phid = $data->getCommitDetail('differential.revisionPHID');
     if ($revision_phid) {
         $props['Differential Revision'] = $handles[$revision_phid]->renderLink();
     }
     $rows = array();
     foreach ($props as $key => $value) {
         $rows[] = '<tr>' . '<th>' . $key . ':</th>' . '<td>' . $value . '</td>' . '</tr>';
     }
     return '<table class="diffusion-commit-properties">' . implode("\n", $rows) . '</table>';
 }
 private function publishFeedStory(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit, PhabricatorRepositoryCommitData $data)
 {
     if (time() > $commit->getEpoch() + 24 * 60 * 60) {
         // Don't publish stories that are more than 24 hours old, to avoid
         // ridiculous levels of feed spam if a repository is imported without
         // disabling feed publishing.
         return;
     }
     $author_phid = $commit->getAuthorPHID();
     $committer_phid = $data->getCommitDetail('committerPHID');
     $publisher = new PhabricatorFeedStoryPublisher();
     $publisher->setStoryType(PhabricatorFeedStoryTypeConstants::STORY_COMMIT);
     $publisher->setStoryData(array('commitPHID' => $commit->getPHID(), 'summary' => $data->getSummary(), 'authorName' => $data->getAuthorName(), 'authorPHID' => $author_phid, 'committerName' => $data->getCommitDetail('committer'), 'committerPHID' => $committer_phid));
     $publisher->setStoryTime($commit->getEpoch());
     $publisher->setRelatedPHIDs(array_filter(array($author_phid, $committer_phid)));
     if ($author_phid) {
         $publisher->setStoryAuthorPHID($author_phid);
     }
     $publisher->publish();
 }
 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.");
     }
     $branches = $data->getCommitDetail('seenOnBranches', array());
     foreach ($branches as $branch) {
         if ($this->shouldAutocloseBranch($branch)) {
             return true;
         }
     }
     return false;
 }
 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;
 }
 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(DiffusionCommitHasTaskEdgeType::EDGECONST, DiffusionCommitHasRevisionEdgeType::EDGECONST, DiffusionCommitRevertsCommitEdgeType::EDGECONST, DiffusionCommitRevertedByCommitEdgeType::EDGECONST));
     $edges = $edge_query->execute();
     $task_phids = array_keys($edges[$commit_phid][DiffusionCommitHasTaskEdgeType::EDGECONST]);
     $revision_phid = key($edges[$commit_phid][DiffusionCommitHasRevisionEdgeType::EDGECONST]);
     $reverts_phids = array_keys($edges[$commit_phid][DiffusionCommitRevertsCommitEdgeType::EDGECONST]);
     $reverted_by_phids = array_keys($edges[$commit_phid][DiffusionCommitRevertedByCommitEdgeType::EDGECONST]);
     $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(" ยท ", $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 ($reverts_phids) {
         $props[pht('Reverts')] = $viewer->renderHandleList($reverts_phids);
     }
     if ($reverted_by_phids) {
         $props[pht('Reverted By')] = $viewer->renderHandleList($reverted_by_phids);
     }
     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;
     }
     return $props;
 }
 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 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.
     }
 }
 /**
  * Find audit requests in the "Auditors" field if it is present and trigger
  * explicit audit requests.
  */
 private function createAuditsFromCommitMessage(PhabricatorRepositoryCommit $commit, PhabricatorRepositoryCommitData $data)
 {
     $message = $data->getCommitMessage();
     $matches = null;
     if (!preg_match('/^Auditors:\\s*(.*)$/im', $message, $matches)) {
         return;
     }
     $phids = DifferentialFieldSpecification::parseCommitMessageObjectList($matches[1], $include_mailables = false, $allow_partial = true);
     if (!$phids) {
         return;
     }
     $requests = id(new PhabricatorRepositoryAuditRequest())->loadAllWhere('commitPHID = %s', $commit->getPHID());
     $requests = mpull($requests, null, 'getAuditorPHID');
     foreach ($phids as $phid) {
         if (isset($requests[$phid])) {
             continue;
         }
         $request = new PhabricatorRepositoryAuditRequest();
         $request->setCommitPHID($commit->getPHID());
         $request->setAuditorPHID($phid);
         $request->setAuditStatus(PhabricatorAuditStatusConstants::AUDIT_REQUESTED);
         $request->setAuditReasons(array('Requested by Author'));
         $request->save();
         $requests[$phid] = $request;
     }
     $commit->updateAuditStatus($requests);
     $commit->save();
 }