protected final function parseCommit(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit) { $viewer = PhabricatorUser::getOmnipotentUser(); $refs_raw = DiffusionQuery::callConduitWithDiffusionRequest($viewer, DiffusionRequest::newFromDictionary(array('repository' => $repository, 'user' => $viewer)), 'diffusion.querycommits', array('repositoryPHID' => $repository->getPHID(), 'phids' => array($commit->getPHID()), 'bypassCache' => true, 'needMessages' => true)); if (empty($refs_raw['data'])) { throw new Exception(pht('Unable to retrieve details for commit "%s"!', $commit->getPHID())); } $ref = DiffusionCommitRef::newFromConduitResult(head($refs_raw['data'])); $this->parseCommitWithRef($repository, $commit, $ref); }
protected function execute(ConduitAPIRequest $request) { $drequest = DiffusionRequest::newFromDictionary(array('user' => $request->getUser(), 'callsign' => $request->getValue('callsign'), 'path' => $request->getValue('path'), 'branch' => $request->getValue('branch'))); $limit = nonempty($request->getValue('limit'), self::DEFAULT_LIMIT); $history_result = DiffusionQuery::callConduitWithDiffusionRequest($request->getUser(), $drequest, 'diffusion.historyquery', array('commit' => $drequest->getCommit(), 'path' => $drequest->getPath(), 'offset' => 0, 'limit' => $limit, 'needDirectChanges' => true, 'needChildChanges' => true)); $history = DiffusionPathChange::newFromConduit($history_result['pathChanges']); $raw_commit_identifiers = mpull($history, 'getCommitIdentifier'); $result = array(); foreach ($raw_commit_identifiers as $id) { $result[] = 'r' . $request->getValue('callsign') . $id; } return $result; }
protected function getSVNResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $results = array(); foreach ($request->getValue('paths') as $path => $commit) { $history_result = DiffusionQuery::callConduitWithDiffusionRequest($request->getUser(), $drequest, 'diffusion.historyquery', array('commit' => $commit, 'path' => $path, 'limit' => 1, 'offset' => 0, 'needDirectChanges' => true, 'needChildChanges' => true)); $history_array = DiffusionPathChange::newFromConduit($history_result['pathChanges']); if ($history_array) { $results[$path] = head($history_array)->getCommit()->getCommitIdentifier(); } } return $results; }
protected final function parseCommit(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit) { if (!$this->shouldSkipImportStep()) { $viewer = PhabricatorUser::getOmnipotentUser(); $refs_raw = DiffusionQuery::callConduitWithDiffusionRequest($viewer, DiffusionRequest::newFromDictionary(array('repository' => $repository, 'user' => $viewer)), 'diffusion.querycommits', array('repositoryPHID' => $repository->getPHID(), 'phids' => array($commit->getPHID()), 'bypassCache' => true, 'needMessages' => true)); if (empty($refs_raw['data'])) { throw new Exception(pht('Unable to retrieve details for commit "%s"!', $commit->getPHID())); } $ref = DiffusionCommitRef::newFromConduitResult(head($refs_raw['data'])); $this->updateCommitData($ref); } if ($this->shouldQueueFollowupTasks()) { $this->queueTask($this->getFollowupTaskClass(), array('commitID' => $commit->getID()), array('priority' => PhabricatorWorker::PRIORITY_DEFAULT)); } }
private function loadCacheKeys(array $paths) { $request = $this->getRequest(); $viewer = $request->getUser(); $repository = $request->getRepository(); $repository_id = $repository->getID(); $last_modified = parent::callConduitWithDiffusionRequest($viewer, $request, 'diffusion.lastmodifiedquery', array('paths' => array_fill_keys($paths, $request->getCommit()))); $map = array(); foreach ($paths as $path) { $identifier = idx($last_modified, $path); if ($identifier === null) { continue; } $path_hash = PhabricatorHash::digestForIndex($path); $map[$path] = "blame({$repository_id}, {$identifier}, {$path_hash}, raw)"; } return $map; }
public function execute(PhutilArgumentParser $args) { $commits = $this->loadCommits($args, 'commits'); if (!$commits) { throw new PhutilArgumentUsageException(pht('Specify one or more commits to resolve users for.')); } $console = PhutilConsole::getConsole(); foreach ($commits as $commit) { $repo = $commit->getRepository(); $name = $repo->formatCommitName($commit->getCommitIdentifier()); $console->writeOut("%s\n", pht('Examining commit %s...', $name)); $refs_raw = DiffusionQuery::callConduitWithDiffusionRequest($this->getViewer(), DiffusionRequest::newFromDictionary(array('repository' => $repo, 'user' => $this->getViewer())), 'diffusion.querycommits', array('repositoryPHID' => $repo->getPHID(), 'phids' => array($commit->getPHID()), 'bypassCache' => true)); if (empty($refs_raw['data'])) { throw new Exception(pht('Unable to retrieve details for commit "%s"!', $commit->getPHID())); } $ref = DiffusionCommitRef::newFromConduitResult(head($refs_raw['data'])); $author = $ref->getAuthor(); $console->writeOut("%s\n", pht('Raw author string: %s', coalesce($author, 'null'))); if ($author !== null) { $handle = $this->resolveUser($commit, $author); if ($handle) { $console->writeOut("%s\n", pht('Phabricator user: %s', $handle->getFullName())); } else { $console->writeOut("%s\n", pht('Unable to resolve a corresponding Phabricator user.')); } } $committer = $ref->getCommitter(); $console->writeOut("%s\n", pht('Raw committer string: %s', coalesce($committer, 'null'))); if ($committer !== null) { $handle = $this->resolveUser($commit, $committer); if ($handle) { $console->writeOut("%s\n", pht('Phabricator user: %s', $handle->getFullName())); } else { $console->writeOut("%s\n", pht('Unable to resolve a corresponding Phabricator user.')); } } } return 0; }
private function getEffectiveCommit(ConduitAPIRequest $request) { if ($this->effectiveCommit === null) { $drequest = $this->getDiffusionRequest(); $path = $drequest->getPath(); $result = DiffusionQuery::callConduitWithDiffusionRequest($request->getUser(), $drequest, 'diffusion.lastmodifiedquery', array('paths' => array($path => $drequest->getStableCommit()))); $this->effectiveCommit = idx($result, $path); } return $this->effectiveCommit; }
private function generateFinalDiff(DifferentialRevision $revision, $actor_phid) { $viewer = PhabricatorUser::getOmnipotentUser(); $drequest = DiffusionRequest::newFromDictionary(array('user' => $viewer, 'repository' => $this->repository)); $raw_diff = DiffusionQuery::callConduitWithDiffusionRequest($viewer, $drequest, 'diffusion.rawdiffquery', array('commit' => $this->commit->getCommitIdentifier())); // TODO: Support adds, deletes and moves under SVN. if (strlen($raw_diff)) { $changes = id(new ArcanistDiffParser())->parseDiff($raw_diff); } else { // This is an empty diff, maybe made with `git commit --allow-empty`. // NOTE: These diffs have the same tree hash as their ancestors, so // they may attach to revisions in an unexpected way. Just let this // happen for now, although it might make sense to special case it // eventually. $changes = array(); } $diff = DifferentialDiff::newFromRawChanges($viewer, $changes)->setRepositoryPHID($this->repository->getPHID())->setAuthorPHID($actor_phid)->setCreationMethod('commit')->setSourceControlSystem($this->repository->getVersionControlSystem())->setLintStatus(DifferentialLintStatus::LINT_AUTO_SKIP)->setUnitStatus(DifferentialUnitStatus::UNIT_AUTO_SKIP)->setDateCreated($this->commit->getEpoch())->setDescription(pht('Commit %s', $this->commit->getMonogram())); $parents = DiffusionQuery::callConduitWithDiffusionRequest($viewer, $drequest, 'diffusion.commitparentsquery', array('commit' => $this->commit->getCommitIdentifier())); if ($parents) { $diff->setSourceControlBaseRevision(head($parents)); } // TODO: Attach binary files. return $diff->save(); }
private function loadParentCommitOf($commit) { $drequest = $this->getDiffusionRequest(); $user = $this->getRequest()->getUser(); $before_req = DiffusionRequest::newFromDictionary(array('user' => $user, 'repository' => $drequest->getRepository(), 'commit' => $commit)); $parents = DiffusionQuery::callConduitWithDiffusionRequest($user, $before_req, 'diffusion.commitparentsquery', array('commit' => $commit)); return head($parents); }
private function loadCommitDiff() { $drequest = DiffusionRequest::newFromDictionary(array('user' => PhabricatorUser::getOmnipotentUser(), 'repository' => $this->repository, 'commit' => $this->commit->getCommitIdentifier())); $byte_limit = self::getEnormousByteLimit(); $raw = DiffusionQuery::callConduitWithDiffusionRequest(PhabricatorUser::getOmnipotentUser(), $drequest, 'diffusion.rawdiffquery', array('commit' => $this->commit->getCommitIdentifier(), 'timeout' => self::getEnormousTimeLimit(), 'byteLimit' => $byte_limit, 'linesOfContext' => 0)); if (strlen($raw) >= $byte_limit) { throw new Exception(pht('The raw text of this change is enormous (larger than %d bytes). ' . 'Herald can not process it.', $byte_limit)); } $parser = new ArcanistDiffParser(); $changes = $parser->parseDiff($raw); $diff = DifferentialDiff::newEphemeralFromRawChanges($changes); return $diff; }
protected function callConduitWithDiffusionRequest($method, array $params = array()) { $user = $this->getRequest()->getUser(); $drequest = $this->getDiffusionRequest(); return DiffusionQuery::callConduitWithDiffusionRequest($user, $drequest, $method, $params); }
private function callConduit($method, array $params) { $viewer = PhabricatorUser::getOmnipotentUser(); $drequest = DiffusionRequest::newFromDictionary(array('user' => $viewer, 'repository' => $this->repository, 'commit' => $this->commit->getCommitIdentifier())); return DiffusionQuery::callConduitWithDiffusionRequest($viewer, $drequest, $method, $params); }
private function resolveRefs(array $refs, array $types) { // First, try to resolve refs from fast cache sources. $cached_query = id(new DiffusionCachedResolveRefsQuery())->setRepository($this->getRepository())->withRefs($refs); if ($types) { $cached_query->withTypes($types); } $cached_results = $cached_query->execute(); // Throw away all the refs we resolved. Hopefully, we'll throw away // everything here. foreach ($refs as $key => $ref) { if (isset($cached_results[$ref])) { unset($refs[$key]); } } // If we couldn't pull everything out of the cache, execute the underlying // VCS operation. if ($refs) { $vcs_results = DiffusionQuery::callConduitWithDiffusionRequest($this->getUser(), $this, 'diffusion.resolverefs', array('types' => $types, 'refs' => $refs)); } else { $vcs_results = array(); } return $vcs_results + $cached_results; }
protected function processDiffusionRequest(AphrontRequest $request) { $viewer = $this->getViewer(); $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); if (!$drequest->supportsBranches()) { return $this->newDialog()->setTitle(pht('No Ref Support'))->appendParagraph(pht('The version control system this repository uses does not ' . 'support named references, so you can not resolve or list ' . 'repository refs in this repository.'))->addCancelButton($repository->getURI()); } $ref_name = $drequest->getBranch(); $cache_query = id(new DiffusionCachedResolveRefsQuery())->setRepository($repository); if ($ref_name !== null) { $cache_query->withRefs(array($ref_name)); } $cache_refs = $cache_query->execute(); $vcs_refs = DiffusionQuery::callConduitWithDiffusionRequest($viewer, $drequest, 'diffusion.resolverefs', array('refs' => array($ref_name))); $all = array(); foreach ($cache_refs as $ref => $results) { foreach ($results as $result) { $id = $result['type'] . '/' . $result['identifier']; $all[$ref][$id]['cache'] = $result; } } foreach ($vcs_refs as $ref => $results) { foreach ($results as $result) { $id = $result['type'] . '/' . $result['identifier']; $all[$ref][$id]['vcs'] = $result; } } $rows = array(); foreach ($all as $ref => $results) { foreach ($results as $info) { $cache = idx($info, 'cache', array()); $vcs = idx($info, 'vcs', array()); $type = idx($vcs, 'type'); if (!$type) { $type = idx($cache, 'type'); } $hash = idx($vcs, 'identifier'); if ($hash !== null) { $hash = DiffusionView::linkCommit($repository, $hash); } $cached_hash = idx($cache, 'identifier'); if ($cached_hash !== null) { $cached_hash = DiffusionView::linkCommit($repository, $cached_hash); } $closed = idx($vcs, 'closed', false); if (!$vcs) { $state = null; } else { $state = $closed ? pht('Closed') : pht('Open'); } $cached_closed = idx($cache, 'closed', false); if (!$cache) { $cached_state = null; } else { $cached_state = $cached_closed ? pht('Closed') : pht('Open'); } $alternate = idx($vcs, 'alternate'); if ($alternate !== null) { $alternate = DiffusionView::linkCommit($repository, $alternate); } $rows[] = array($ref, $type, $hash, $cached_hash, $state, $cached_state, $alternate); } } $table = id(new AphrontTableView($rows))->setHeaders(array(pht('Ref'), pht('Type'), pht('Hash'), pht('Cached Hash'), pht('State'), pht('Cached State'), pht('Alternate'))); $content = id(new PHUIObjectBoxView())->setHeaderText(pht('Ref "%s"', $ref_name))->setTable($table); $crumbs = $this->buildCrumbs(array()); $crumbs->addTextCrumb(pht('Refs')); return $this->buildApplicationPage(array($crumbs, $content), array('title' => array(pht('Refs'), $repository->getMonogram(), $ref_name))); }
public function isDiffChangedBeforeCommit(PhabricatorRepositoryCommit $commit, DifferentialDiff $old, DifferentialDiff $new) { $viewer = $this->getViewer(); $repository = $commit->getRepository(); $identifier = $commit->getCommitIdentifier(); $vs_changesets = array(); foreach ($old->getChangesets() as $changeset) { $path = $changeset->getAbsoluteRepositoryPath($repository, $old); $path = ltrim($path, '/'); $vs_changesets[$path] = $changeset; } $changesets = array(); foreach ($new->getChangesets() as $changeset) { $path = $changeset->getAbsoluteRepositoryPath($repository, $new); $path = ltrim($path, '/'); $changesets[$path] = $changeset; } if (array_fill_keys(array_keys($changesets), true) != array_fill_keys(array_keys($vs_changesets), true)) { return true; } $file_phids = array(); foreach ($vs_changesets as $changeset) { $metadata = $changeset->getMetadata(); $file_phid = idx($metadata, 'new:binary-phid'); if ($file_phid) { $file_phids[$file_phid] = $file_phid; } } $files = array(); if ($file_phids) { $files = id(new PhabricatorFileQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withPHIDs($file_phids)->execute(); $files = mpull($files, null, 'getPHID'); } foreach ($changesets as $path => $changeset) { $vs_changeset = $vs_changesets[$path]; $file_phid = idx($vs_changeset->getMetadata(), 'new:binary-phid'); if ($file_phid) { if (!isset($files[$file_phid])) { return true; } $drequest = DiffusionRequest::newFromDictionary(array('user' => $viewer, 'repository' => $repository)); $response = DiffusionQuery::callConduitWithDiffusionRequest($viewer, $drequest, 'diffusion.filecontentquery', array('commit' => $identifier, 'path' => $path)); $new_file_phid = $response['filePHID']; if (!$new_file_phid) { return true; } $new_file = id(new PhabricatorFileQuery())->setViewer($viewer)->withPHIDs(array($new_file_phid))->executeOne(); if (!$new_file) { return true; } if ($files[$file_phid]->loadFileData() != $new_file->loadFileData()) { return true; } } else { $context = implode("\n", $changeset->makeChangesWithContext()); $vs_context = implode("\n", $vs_changeset->makeChangesWithContext()); // We couldn't just compare $context and $vs_context because following // diffs will be considered different: // // -(empty line) // -echo 'test'; // (empty line) // // (empty line) // -echo "test"; // -(empty line) $hunk = id(new DifferentialModernHunk())->setChanges($context); $vs_hunk = id(new DifferentialModernHunk())->setChanges($vs_context); if ($hunk->makeOldFile() != $vs_hunk->makeOldFile() || $hunk->makeNewFile() != $vs_hunk->makeNewFile()) { return true; } } } return false; }
private function resolveRefs(array $refs) { if ($this->shouldInitFromConduit()) { return DiffusionQuery::callConduitWithDiffusionRequest($this->getUser(), $this, 'diffusion.resolverefs', array('refs' => $refs)); } else { return id(new DiffusionLowLevelResolveRefsQuery())->setRepository($this->getRepository())->withRefs($refs)->execute(); } }
public function save() { if ($this->getID()) { $is_new = false; } else { $is_new = true; } $this->openTransaction(); $ret = parent::save(); $add_owners = array(); $remove_owners = array(); $all_owners = array(); if ($this->unsavedOwners) { $new_owners = array_fill_keys($this->unsavedOwners, true); $cur_owners = array(); foreach ($this->loadOwners() as $owner) { if (empty($new_owners[$owner->getUserPHID()])) { $remove_owners[$owner->getUserPHID()] = true; $owner->delete(); continue; } $cur_owners[$owner->getUserPHID()] = true; } $add_owners = array_diff_key($new_owners, $cur_owners); $all_owners = array_merge(array($this->getPrimaryOwnerPHID() => true), $new_owners, $remove_owners); foreach ($add_owners as $phid => $ignored) { $owner = new PhabricatorOwnersOwner(); $owner->setPackageID($this->getID()); $owner->setUserPHID($phid); $owner->save(); } unset($this->unsavedOwners); } $add_paths = array(); $remove_paths = array(); $touched_repos = array(); if ($this->unsavedPaths) { $new_paths = igroup($this->unsavedPaths, 'repositoryPHID', 'path'); $cur_paths = $this->loadPaths(); foreach ($cur_paths as $key => $path) { $repository_phid = $path->getRepositoryPHID(); $new_path = head(idx(idx($new_paths, $repository_phid, array()), $path->getPath(), array())); $excluded = $path->getExcluded(); if (!$new_path || idx($new_path, 'excluded') != $excluded) { $touched_repos[$repository_phid] = true; $remove_paths[$repository_phid][$path->getPath()] = $excluded; $path->delete(); unset($cur_paths[$key]); } } $cur_paths = mgroup($cur_paths, 'getRepositoryPHID', 'getPath'); foreach ($new_paths as $repository_phid => $paths) { // TODO: (T603) Thread policy stuff in here. // get repository object for path validation $repository = id(new PhabricatorRepository())->loadOneWhere('phid = %s', $repository_phid); if (!$repository) { continue; } foreach ($paths as $path => $dicts) { $path = ltrim($path, '/'); // build query to validate path $drequest = DiffusionRequest::newFromDictionary(array('user' => $this->getActor(), 'repository' => $repository, 'path' => $path)); $results = DiffusionBrowseResultSet::newFromConduit(DiffusionQuery::callConduitWithDiffusionRequest($this->getActor(), $drequest, 'diffusion.browsequery', array('commit' => $drequest->getCommit(), 'path' => $path, 'needValidityOnly' => true))); $valid = $results->isValidResults(); $is_directory = true; if (!$valid) { switch ($results->getReasonForEmptyResultSet()) { case DiffusionBrowseResultSet::REASON_IS_FILE: $valid = true; $is_directory = false; break; case DiffusionBrowseResultSet::REASON_IS_EMPTY: $valid = true; break; } } if ($is_directory && substr($path, -1) != '/') { $path .= '/'; } if (substr($path, 0, 1) != '/') { $path = '/' . $path; } if (empty($cur_paths[$repository_phid][$path]) && $valid) { $touched_repos[$repository_phid] = true; $excluded = idx(reset($dicts), 'excluded', 0); $add_paths[$repository_phid][$path] = $excluded; $obj = new PhabricatorOwnersPath(); $obj->setPackageID($this->getID()); $obj->setRepositoryPHID($repository_phid); $obj->setPath($path); $obj->setExcluded($excluded); $obj->save(); } } } unset($this->unsavedPaths); } $this->saveTransaction(); if ($is_new) { $mail = new PackageCreateMail($this); } else { $mail = new PackageModifyMail($this, array_keys($add_owners), array_keys($remove_owners), array_keys($all_owners), array_keys($touched_repos), $add_paths, $remove_paths); } $mail->setActor($this->getActor()); $mail->send(); return $ret; }
protected function getResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $path_dicts = $request->getValue('paths', array()); $paths = array(); foreach ($path_dicts as $dict) { $paths[] = DiffusionRepositoryPath::newFromDictionary($dict); } $best = -1; $readme = ''; $best_render_type = 'plain'; foreach ($paths as $result_path) { $file_type = $result_path->getFileType(); if ($file_type != ArcanistDiffChangeType::FILE_NORMAL && $file_type != ArcanistDiffChangeType::FILE_TEXT) { // Skip directories, etc. continue; } $path = strtolower($result_path->getPath()); if ($path === 'readme') { $path .= '.remarkup'; } if (strncmp($path, 'readme.', 7) !== 0) { continue; } $priority = 0; switch (substr($path, 7)) { case 'remarkup': $priority = 100; $render_type = 'remarkup'; break; case 'rainbow': $priority = 90; $render_type = 'rainbow'; break; case 'md': $priority = 50; $render_type = 'remarkup'; break; case 'txt': $priority = 10; $render_type = 'plain'; break; default: $priority = 0; $render_type = 'plain'; break; } if ($priority > $best) { $best = $priority; $readme = $result_path; $best_render_type = $render_type; } } if (!$readme) { return ''; } $readme_request = DiffusionRequest::newFromDictionary(array('user' => $request->getUser(), 'repository' => $drequest->getRepository(), 'commit' => $drequest->getStableCommit(), 'path' => $readme->getFullPath())); $file_content = DiffusionFileContent::newFromConduit(DiffusionQuery::callConduitWithDiffusionRequest($request->getUser(), $readme_request, 'diffusion.filecontentquery', array('commit' => $drequest->getStableCommit(), 'path' => $readme->getFullPath(), 'needsBlame' => false))); $readme_content = $file_content->getCorpus(); switch ($best_render_type) { case 'plain': $readme_content = phutil_escape_html_newlines($readme_content); $class = null; break; case 'rainbow': $highlighter = new PhutilRainbowSyntaxHighlighter(); $readme_content = $highlighter->getHighlightFuture($readme_content)->resolve(); $readme_content = phutil_escape_html_newlines($readme_content); require_celerity_resource('syntax-highlighting-css'); $class = 'remarkup-code'; break; case 'remarkup': // TODO: This is sketchy, but make sure we hit the markup cache. $markup_object = id(new PhabricatorMarkupOneOff())->setEngineRuleset('diffusion-readme')->setContent($readme_content); $markup_field = 'default'; $readme_content = id(new PhabricatorMarkupEngine())->setViewer($request->getUser())->addObject($markup_object, $markup_field)->process()->getOutput($markup_object, $markup_field); $engine = $markup_object->newMarkupEngine($markup_field); $toc = PhutilRemarkupHeaderBlockRule::renderTableOfContents($engine); if ($toc) { $toc = phutil_tag_div('phabricator-remarkup-toc', array(phutil_tag_div('phabricator-remarkup-toc-header', pht('Table of Contents')), $toc)); $readme_content = array($toc, $readme_content); } $class = 'phabricator-remarkup'; break; } $readme_content = phutil_tag('div', array('class' => $class), $readme_content); return $readme_content; }
protected function parseCommitChanges(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit) { $viewer = PhabricatorUser::getOmnipotentUser(); $raw = DiffusionQuery::callConduitWithDiffusionRequest($viewer, DiffusionRequest::newFromDictionary(array('repository' => $repository, 'user' => $viewer)), 'diffusion.internal.gitrawdiffquery', array('commit' => $commit->getCommitIdentifier())); $changes = array(); $move_away = array(); $copy_away = array(); $lines = explode("\n", $raw); foreach ($lines as $line) { if (!strlen(trim($line))) { continue; } list($old_mode, $new_mode, $old_hash, $new_hash, $more_stuff) = preg_split('/ +/', $line, 5); // We may only have two pieces here. list($action, $src_path, $dst_path) = array_merge(explode("\t", $more_stuff), array(null)); // Normalize the paths for consistency with the SVN workflow. $src_path = '/' . $src_path; if ($dst_path) { $dst_path = '/' . $dst_path; } $old_mode = intval($old_mode, 8); $new_mode = intval($new_mode, 8); switch ($new_mode & 0160000) { case 0160000: $file_type = DifferentialChangeType::FILE_SUBMODULE; break; case 0120000: $file_type = DifferentialChangeType::FILE_SYMLINK; break; case 040000: $file_type = DifferentialChangeType::FILE_DIRECTORY; break; default: $file_type = DifferentialChangeType::FILE_NORMAL; break; } // TODO: We can detect binary changes as git does, through a combination // of running 'git check-attr' for stuff like 'binary', 'merge' or 'diff', // and by falling back to inspecting the first 8,000 characters of the // buffer for null bytes (this is seriously git's algorithm, see // buffer_is_binary() in xdiff-interface.c). $change_type = null; $change_path = $src_path; $change_target = null; $is_direct = true; switch ($action[0]) { case 'A': $change_type = DifferentialChangeType::TYPE_ADD; break; case 'D': $change_type = DifferentialChangeType::TYPE_DELETE; break; case 'C': $change_type = DifferentialChangeType::TYPE_COPY_HERE; $change_path = $dst_path; $change_target = $src_path; $copy_away[$change_target][] = $change_path; break; case 'R': $change_type = DifferentialChangeType::TYPE_MOVE_HERE; $change_path = $dst_path; $change_target = $src_path; $move_away[$change_target][] = $change_path; break; case 'T': // Type of the file changed, fall through and treat it as a // modification. Not 100% sure this is the right thing to do but it // seems reasonable. // Type of the file changed, fall through and treat it as a // modification. Not 100% sure this is the right thing to do but it // seems reasonable. case 'M': if ($file_type == DifferentialChangeType::FILE_DIRECTORY) { $change_type = DifferentialChangeType::TYPE_CHILD; $is_direct = false; } else { $change_type = DifferentialChangeType::TYPE_CHANGE; } break; // NOTE: "U" (unmerged) and "X" (unknown) statuses are also possible // in theory but shouldn't appear here. // NOTE: "U" (unmerged) and "X" (unknown) statuses are also possible // in theory but shouldn't appear here. default: throw new Exception(pht("Failed to parse line '%s'.", $line)); } $changes[$change_path] = array('repositoryID' => $repository->getID(), 'commitID' => $commit->getID(), 'path' => $change_path, 'changeType' => $change_type, 'fileType' => $file_type, 'isDirect' => $is_direct, 'commitSequence' => $commit->getEpoch(), 'targetPath' => $change_target, 'targetCommitID' => $change_target ? $commit->getID() : null); } // Add a change to '/' since git doesn't mention it. $changes['/'] = array('repositoryID' => $repository->getID(), 'commitID' => $commit->getID(), 'path' => '/', 'changeType' => DifferentialChangeType::TYPE_CHILD, 'fileType' => DifferentialChangeType::FILE_DIRECTORY, 'isDirect' => false, 'commitSequence' => $commit->getEpoch(), 'targetPath' => null, 'targetCommitID' => null); foreach ($copy_away as $change_path => $destinations) { if (isset($move_away[$change_path])) { $change_type = DifferentialChangeType::TYPE_MULTICOPY; $is_direct = true; unset($move_away[$change_path]); } else { $change_type = DifferentialChangeType::TYPE_COPY_AWAY; // This change is direct if we picked up a modification above (i.e., // the original copy source was also edited). Otherwise the original // wasn't touched, so leave it as an indirect change. $is_direct = isset($changes[$change_path]); } $reference = $changes[reset($destinations)]; $changes[$change_path] = array('repositoryID' => $repository->getID(), 'commitID' => $commit->getID(), 'path' => $change_path, 'changeType' => $change_type, 'fileType' => $reference['fileType'], 'isDirect' => $is_direct, 'commitSequence' => $commit->getEpoch(), 'targetPath' => null, 'targetCommitID' => null); } foreach ($move_away as $change_path => $destinations) { $reference = $changes[reset($destinations)]; $changes[$change_path] = array('repositoryID' => $repository->getID(), 'commitID' => $commit->getID(), 'path' => $change_path, 'changeType' => DifferentialChangeType::TYPE_MOVE_AWAY, 'fileType' => $reference['fileType'], 'isDirect' => true, 'commitSequence' => $commit->getEpoch(), 'targetPath' => null, 'targetCommitID' => null); } $paths = array(); foreach ($changes as $change) { $paths[$change['path']] = true; if ($change['targetPath']) { $paths[$change['targetPath']] = true; } } $path_map = $this->lookupOrCreatePaths(array_keys($paths)); foreach ($changes as $key => $change) { $changes[$key]['pathID'] = $path_map[$change['path']]; if ($change['targetPath']) { $changes[$key]['targetPathID'] = $path_map[$change['targetPath']]; } else { $changes[$key]['targetPathID'] = null; } } $results = array(); foreach ($changes as $change) { $result = id(new PhabricatorRepositoryParsedChange())->setPathID($change['pathID'])->setTargetPathID($change['targetPathID'])->setTargetCommitID($change['targetCommitID'])->setChangeType($change['changeType'])->setFileType($change['fileType'])->setIsDirect($change['isDirect'])->setCommitSequence($change['commitSequence']); $results[] = $result; } return $results; }