public function buildPatch()
 {
     $diff = new DifferentialDiff();
     $diff->attachChangesets($this->getChangesets());
     $raw_changes = $diff->buildChangesList();
     $changes = array();
     foreach ($raw_changes as $changedict) {
         $changes[] = ArcanistDiffChange::newFromDictionary($changedict);
     }
     $viewer = $this->getViewer();
     $loader = id(new PhabricatorFileBundleLoader())->setViewer($viewer);
     $bundle = ArcanistBundle::newFromChanges($changes);
     $bundle->setLoadFileDataCallback(array($loader, 'loadFileData'));
     $format = $this->getFormat();
     switch ($format) {
         case 'git':
             return $bundle->toGitPatch();
             break;
         case 'unified':
         default:
             return $bundle->toUnifiedDiff();
             break;
     }
 }
 private function buildDependencyGraph(ArcanistBundle $bundle)
 {
     $graph = null;
     if ($this->getRepositoryAPI() instanceof ArcanistSubversionAPI) {
         return $graph;
     }
     $revision_id = $bundle->getRevisionID();
     if ($revision_id) {
         $revisions = $this->getConduit()->callMethodSynchronous('differential.query', array('ids' => array($revision_id)));
         if ($revisions) {
             $revision = head($revisions);
             $rev_auxiliary = idx($revision, 'auxiliary', array());
             $phids = idx($rev_auxiliary, 'phabricator:depends-on', array());
             if ($phids) {
                 $revision_phid = $revision['phid'];
                 $graph = id(new ArcanistDifferentialDependencyGraph())->setConduit($this->getConduit())->setRepositoryAPI($this->getRepositoryAPI())->setStartPHID($revision_phid)->addNodes(array($revision_phid => $phids))->loadGraph();
             }
         }
     }
     return $graph;
 }
 /**
  * Note this code is somewhat similar to the buildPatch method in
  * @{class:DifferentialReviewRequestMail}.
  *
  * @return @{class:AphrontRedirectResponse}
  */
 private function buildRawDiffResponse(DifferentialRevision $revision, array $changesets, array $vs_changesets, array $vs_map, PhabricatorRepository $repository = null)
 {
     assert_instances_of($changesets, 'DifferentialChangeset');
     assert_instances_of($vs_changesets, 'DifferentialChangeset');
     $viewer = $this->getRequest()->getUser();
     id(new DifferentialHunkQuery())->setViewer($viewer)->withChangesets($changesets)->needAttachToChangesets(true)->execute();
     $diff = new DifferentialDiff();
     $diff->attachChangesets($changesets);
     $raw_changes = $diff->buildChangesList();
     $changes = array();
     foreach ($raw_changes as $changedict) {
         $changes[] = ArcanistDiffChange::newFromDictionary($changedict);
     }
     $loader = id(new PhabricatorFileBundleLoader())->setViewer($viewer);
     $bundle = ArcanistBundle::newFromChanges($changes);
     $bundle->setLoadFileDataCallback(array($loader, 'loadFileData'));
     $vcs = $repository ? $repository->getVersionControlSystem() : null;
     switch ($vcs) {
         case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
             $raw_diff = $bundle->toGitPatch();
             break;
         case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         default:
             $raw_diff = $bundle->toUnifiedDiff();
             break;
     }
     $request_uri = $this->getRequest()->getRequestURI();
     // this ends up being something like
     //   D123.diff
     // or the verbose
     //   D123.vs123.id123.whitespaceignore-all.diff
     // lame but nice to include these options
     $file_name = ltrim($request_uri->getPath(), '/') . '.';
     foreach ($request_uri->getQueryParams() as $key => $value) {
         if ($key == 'download') {
             continue;
         }
         $file_name .= $key . $value . '.';
     }
     $file_name .= 'diff';
     $file = PhabricatorFile::buildFromFileDataOrHash($raw_diff, array('name' => $file_name, 'ttl' => 60 * 60 * 24, 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE));
     $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
     $file->attachToObject($revision->getPHID());
     unset($unguarded);
     return $file->getRedirectResponse();
 }
 /**
  * Do the best we can to prevent PEBKAC and id10t issues.
  */
 private function sanityCheck(ArcanistBundle $bundle)
 {
     $repository_api = $this->getRepositoryAPI();
     // Require clean working copy
     $this->requireCleanWorkingCopy();
     // Check to see if the bundle's project id matches the working copy
     // project id
     $bundle_project_id = $bundle->getProjectID();
     $working_copy_project_id = $this->getWorkingCopy()->getProjectID();
     if (empty($bundle_project_id)) {
         // this means $source is SOURCE_PATCH || SOURCE_BUNDLE w/ $version = 0
         // they don't come with a project id so just do nothing
     } else {
         if ($bundle_project_id != $working_copy_project_id) {
             if ($working_copy_project_id) {
                 $issue = "This patch is for the '{$bundle_project_id}' project,  but the " . "working copy belongs to the '{$working_copy_project_id}' project.";
             } else {
                 $issue = "This patch is for the '{$bundle_project_id}' project, but the " . "working copy does not have an '.arcconfig' file to identify which " . "project it belongs to.";
             }
             $ok = phutil_console_confirm("{$issue} Still try to apply the patch?", $default_no = false);
             if (!$ok) {
                 throw new ArcanistUserAbortException();
             }
         }
     }
     // Check to see if the bundle's base revision matches the working copy
     // base revision
     if ($repository_api->supportsLocalCommits()) {
         $bundle_base_rev = $bundle->getBaseRevision();
         if (empty($bundle_base_rev)) {
             // this means $source is SOURCE_PATCH || SOURCE_BUNDLE w/ $version < 2
             // they don't have a base rev so just do nothing
             $commit_exists = true;
         } else {
             $commit_exists = $repository_api->hasLocalCommit($bundle_base_rev);
         }
         if (!$commit_exists) {
             // we have a problem...! lots of work because we need to ask
             // differential for revision information for these base revisions
             // to improve our error message.
             $bundle_base_rev_str = null;
             $source_base_rev = $repository_api->getWorkingCopyRevision();
             $source_base_rev_str = null;
             if ($repository_api instanceof ArcanistGitAPI) {
                 $hash_type = ArcanistDifferentialRevisionHash::HASH_GIT_COMMIT;
             } else {
                 if ($repository_api instanceof ArcanistMercurialAPI) {
                     $hash_type = ArcanistDifferentialRevisionHash::HASH_MERCURIAL_COMMIT;
                 } else {
                     $hash_type = null;
                 }
             }
             if ($hash_type) {
                 // 2 round trips because even though we could send off one query
                 // we wouldn't be able to tell which revisions were for which hash
                 $hash = array($hash_type, $bundle_base_rev);
                 $bundle_revision = $this->loadRevisionFromHash($hash);
                 $hash = array($hash_type, $source_base_rev);
                 $source_revision = $this->loadRevisionFromHash($hash);
                 if ($bundle_revision) {
                     $bundle_base_rev_str = $bundle_base_rev . ' \\ D' . $bundle_revision['id'];
                 }
                 if ($source_revision) {
                     $source_base_rev_str = $source_base_rev . ' \\ D' . $source_revision['id'];
                 }
             }
             $bundle_base_rev_str = nonempty($bundle_base_rev_str, $bundle_base_rev);
             $source_base_rev_str = nonempty($source_base_rev_str, $source_base_rev);
             $ok = phutil_console_confirm("This diff is against commit {$bundle_base_rev_str}, but the " . "commit is nowhere in the working copy. Try to apply it against " . "the current working copy state? ({$source_base_rev_str})", $default_no = false);
             if (!$ok) {
                 throw new ArcanistUserAbortException();
             }
         }
     }
     // TODO -- more sanity checks here
 }
 private function loadBundleFromConduit(ConduitClient $conduit, $params)
 {
     $future = $conduit->callMethod('differential.getdiff', $params);
     $diff = $future->resolve();
     $changes = array();
     foreach ($diff['changes'] as $changedict) {
         $changes[] = ArcanistDiffChange::newFromDictionary($changedict);
     }
     $bundle = ArcanistBundle::newFromChanges($changes);
     $bundle->setConduit($conduit);
     // since the conduit method has changes, assume that these fields
     // could be unset
     $bundle->setProjectID(idx($diff, 'projectName'));
     $bundle->setBaseRevision(idx($diff, 'sourceControlBaseRevision'));
     $bundle->setRevisionID(idx($diff, 'revisionID'));
     $bundle->setAuthorName(idx($diff, 'authorName'));
     $bundle->setAuthorEmail(idx($diff, 'authorEmail'));
     return $bundle;
 }
 private function loadBundleFromConduit(ConduitClient $conduit, $params)
 {
     $future = $conduit->callMethod('differential.getdiff', $params);
     $diff = $future->resolve();
     $changes = array();
     foreach ($diff['changes'] as $changedict) {
         $changes[] = ArcanistDiffChange::newFromDictionary($changedict);
     }
     $bundle = ArcanistBundle::newFromChanges($changes);
     $bundle->setConduit($conduit);
     $bundle->setProjectID($diff['projectName']);
     $bundle->setBaseRevision($diff['sourceControlBaseRevision']);
     $bundle->setRevisionID($diff['revisionID']);
     return $bundle;
 }
 public function run()
 {
     $source = $this->getSource();
     switch ($source) {
         case self::SOURCE_LOCAL:
             $repository_api = $this->getRepositoryAPI();
             $parser = new ArcanistDiffParser();
             $parser->setRepositoryAPI($repository_api);
             if ($repository_api instanceof ArcanistGitAPI) {
                 $this->parseBaseCommitArgument($this->getArgument('paths'));
                 $diff = $repository_api->getFullGitDiff($repository_api->getBaseCommit(), $repository_api->getHeadCommit());
                 $changes = $parser->parseDiff($diff);
                 $authors = $this->getConduit()->callMethodSynchronous('user.query', array('phids' => array($this->getUserPHID())));
                 $author_dict = reset($authors);
                 list($email) = $repository_api->execxLocal('config user.email');
                 $author = sprintf('%s <%s>', $author_dict['realName'], $email);
             } else {
                 if ($repository_api instanceof ArcanistMercurialAPI) {
                     $this->parseBaseCommitArgument($this->getArgument('paths'));
                     $diff = $repository_api->getFullMercurialDiff();
                     $changes = $parser->parseDiff($diff);
                     $authors = $this->getConduit()->callMethodSynchronous('user.query', array('phids' => array($this->getUserPHID())));
                     list($author) = $repository_api->execxLocal('showconfig ui.username');
                 } else {
                     // TODO: paths support
                     $paths = $repository_api->getWorkingCopyStatus();
                     $changes = $parser->parseSubversionDiff($repository_api, $paths);
                     $author = $this->getUserName();
                 }
             }
             $bundle = ArcanistBundle::newFromChanges($changes);
             $bundle->setProjectID($this->getWorkingCopy()->getProjectID());
             $bundle->setBaseRevision($repository_api->getSourceControlBaseRevision());
             // NOTE: we can't get a revision ID for SOURCE_LOCAL
             $parser = new PhutilEmailAddress($author);
             $bundle->setAuthorName($parser->getDisplayName());
             $bundle->setAuthorEmail($parser->getAddress());
             break;
         case self::SOURCE_REVISION:
             $bundle = $this->loadRevisionBundleFromConduit($this->getConduit(), $this->getSourceID());
             break;
         case self::SOURCE_DIFF:
             $bundle = $this->loadDiffBundleFromConduit($this->getConduit(), $this->getSourceID());
             break;
     }
     $try_encoding = nonempty($this->getArgument('encoding'), null);
     if (!$try_encoding) {
         try {
             $project_info = $this->getConduit()->callMethodSynchronous('arcanist.projectinfo', array('name' => $bundle->getProjectID()));
             $try_encoding = $project_info['encoding'];
         } catch (ConduitClientException $e) {
             $try_encoding = null;
         }
     }
     if ($try_encoding) {
         $bundle->setEncoding($try_encoding);
     }
     $format = $this->getFormat();
     switch ($format) {
         case self::FORMAT_GIT:
             echo $bundle->toGitPatch();
             break;
         case self::FORMAT_UNIFIED:
             echo $bundle->toUnifiedDiff();
             break;
         case self::FORMAT_BUNDLE:
             $path = $this->getArgument('arcbundle');
             echo "Writing bundle to '{$path}'...\n";
             $bundle->writeToDisk($path);
             echo "done.\n";
             break;
     }
     return 0;
 }
 public static function newFromArcBundle($path)
 {
     $path = Filesystem::resolvePath($path);
     $future = new ExecFuture('tar tfO %s', $path);
     list($stdout, $file_list) = $future->resolvex();
     $file_list = explode("\n", trim($file_list));
     if (in_array('meta.json', $file_list)) {
         $future = new ExecFuture('tar xfO %s meta.json', $path);
         $meta_info = $future->resolveJSON();
         $version = idx($meta_info, 'version', 0);
         $project_name = idx($meta_info, 'projectName');
         $base_revision = idx($meta_info, 'baseRevision');
         $revision_id = idx($meta_info, 'revisionID');
         $encoding = idx($meta_info, 'encoding');
         $author_name = idx($meta_info, 'authorName');
         $author_email = idx($meta_info, 'authorEmail');
     } else {
         // this arc bundle was probably made before we started storing meta info
         $version = 0;
         $project_name = null;
         $base_revision = null;
         $revision_id = null;
         $encoding = null;
         $author = null;
     }
     $future = new ExecFuture('tar xfO %s changes.json', $path);
     $changes = $future->resolveJSON();
     foreach ($changes as $change_key => $change) {
         foreach ($change['hunks'] as $key => $hunk) {
             list($hunk_data) = execx('tar xfO %s hunks/%s', $path, $hunk['corpus']);
             $changes[$change_key]['hunks'][$key]['corpus'] = $hunk_data;
         }
     }
     foreach ($changes as $change_key => $change) {
         $changes[$change_key] = ArcanistDiffChange::newFromDictionary($change);
     }
     $obj = new ArcanistBundle();
     $obj->changes = $changes;
     $obj->diskPath = $path;
     $obj->setProjectID($project_name);
     $obj->setBaseRevision($base_revision);
     $obj->setRevisionID($revision_id);
     $obj->setEncoding($encoding);
     return $obj;
 }
 /**
  * Note this code is somewhat similar to the buildPatch method in
  * @{class:DifferentialReviewRequestMail}.
  *
  * @return @{class:AphrontRedirectResponse}
  */
 private function buildRawDiffResponse(array $changesets, array $vs_changesets, array $vs_map, PhabricatorRepository $repository = null)
 {
     assert_instances_of($changesets, 'DifferentialChangeset');
     assert_instances_of($vs_changesets, 'DifferentialChangeset');
     $engine = new PhabricatorDifferenceEngine();
     $generated_changesets = array();
     foreach ($changesets as $changeset) {
         $changeset->attachHunks($changeset->loadHunks());
         $right = $changeset->makeNewFile();
         $choice = $changeset;
         $vs = idx($vs_map, $changeset->getID());
         if ($vs == -1) {
             $left = $right;
             $right = $changeset->makeOldFile();
         } else {
             if ($vs) {
                 $choice = $vs_changeset = $vs_changesets[$vs];
                 $vs_changeset->attachHunks($vs_changeset->loadHunks());
                 $left = $vs_changeset->makeNewFile();
             } else {
                 $left = $changeset->makeOldFile();
             }
         }
         $synthetic = $engine->generateChangesetFromFileContent($left, $right);
         if (!$synthetic->getAffectedLineCount()) {
             $filetype = $choice->getFileType();
             if ($filetype == DifferentialChangeType::FILE_TEXT || $filetype == DifferentialChangeType::FILE_SYMLINK) {
                 continue;
             }
         }
         $choice->attachHunks($synthetic->getHunks());
         $generated_changesets[] = $choice;
     }
     $diff = new DifferentialDiff();
     $diff->attachChangesets($generated_changesets);
     $diff_dict = $diff->getDiffDict();
     $changes = array();
     foreach ($diff_dict['changes'] as $changedict) {
         $changes[] = ArcanistDiffChange::newFromDictionary($changedict);
     }
     $bundle = ArcanistBundle::newFromChanges($changes);
     $bundle->setLoadFileDataCallback(array($this, 'loadFileByPHID'));
     $vcs = $repository ? $repository->getVersionControlSystem() : null;
     switch ($vcs) {
         case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
             $raw_diff = $bundle->toGitPatch();
             break;
         case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         default:
             $raw_diff = $bundle->toUnifiedDiff();
             break;
     }
     $request_uri = $this->getRequest()->getRequestURI();
     // this ends up being something like
     //   D123.diff
     // or the verbose
     //   D123.vs123.id123.whitespaceignore-all.diff
     // lame but nice to include these options
     $file_name = ltrim($request_uri->getPath(), '/') . '.';
     foreach ($request_uri->getQueryParams() as $key => $value) {
         if ($key == 'download') {
             continue;
         }
         $file_name .= $key . $value . '.';
     }
     $file_name .= 'diff';
     $file = PhabricatorFile::buildFromFileDataOrHash($raw_diff, array('name' => $file_name));
     return id(new AphrontRedirectResponse())->setURI($file->getBestURI());
 }
 protected function buildAttachments()
 {
     $attachments = array();
     if (PhabricatorEnv::getEnvConfig('metamta.differential.attach-patches')) {
         $revision = $this->getRevision();
         $revision_id = $revision->getID();
         $diffs = $revision->loadDiffs();
         $diff_number = count($diffs);
         $diff = array_pop($diffs);
         $filename = "D{$revision_id}.{$diff_number}.patch";
         $diff->attachChangesets($diff->loadChangesets());
         // TODO: We could batch this to improve performance.
         foreach ($diff->getChangesets() as $changeset) {
             $changeset->attachHunks($changeset->loadHunks());
         }
         $diff_dict = $diff->getDiffDict();
         $changes = array();
         foreach ($diff_dict['changes'] as $changedict) {
             $changes[] = ArcanistDiffChange::newFromDictionary($changedict);
         }
         $bundle = ArcanistBundle::newFromChanges($changes);
         $unified_diff = $bundle->toUnifiedDiff();
         $attachments[] = new PhabricatorMetaMTAAttachment($unified_diff, $filename, 'text/x-patch; charset=utf-8');
     }
     return $attachments;
 }
 public function run()
 {
     $source = $this->getSource();
     switch ($source) {
         case self::SOURCE_LOCAL:
             $repository_api = $this->getRepositoryAPI();
             $parser = new ArcanistDiffParser();
             if ($repository_api instanceof ArcanistGitAPI) {
                 $repository_api->parseRelativeLocalCommit($this->getArgument('paths'));
                 $diff = $repository_api->getFullGitDiff();
                 $changes = $parser->parseDiff($diff);
             } else {
                 // TODO: paths support
                 $paths = $repository_api->getWorkingCopyStatus();
                 $changes = $parser->parseSubversionDiff($repository_api, $paths);
             }
             $bundle = ArcanistBundle::newFromChanges($changes);
             $bundle->setProjectID($this->getWorkingCopy()->getProjectID());
             $bundle->setBaseRevision($repository_api->getSourceControlBaseRevision());
             // note we can't get a revision ID for SOURCE_LOCAL
             break;
         case self::SOURCE_REVISION:
             $bundle = $this->loadRevisionBundleFromConduit($this->getConduit(), $this->getSourceID());
             break;
         case self::SOURCE_DIFF:
             $bundle = $this->loadDiffBundleFromConduit($this->getConduit(), $this->getSourceID());
             break;
     }
     $try_encoding = nonempty($this->getArgument('encoding'), null);
     if (!$try_encoding) {
         try {
             $try_encoding = $this->getRepositoryEncoding();
         } catch (ConduitClientException $e) {
             $try_encoding = null;
         }
     }
     if ($try_encoding) {
         $bundle->setEncoding($try_encoding);
     }
     $format = $this->getFormat();
     switch ($format) {
         case self::FORMAT_GIT:
             echo $bundle->toGitPatch();
             break;
         case self::FORMAT_UNIFIED:
             echo $bundle->toUnifiedDiff();
             break;
         case self::FORMAT_BUNDLE:
             $path = $this->getArgument('arcbundle');
             echo "Writing bundle to '{$path}'...\n";
             $bundle->writeToDisk($path);
             echo "done.\n";
             break;
     }
     return 0;
 }
 private function loadOneChangeBundle($old, $new)
 {
     $diff = $this->loadDiff($old, $new);
     return ArcanistBundle::newFromDiff($diff);
 }
 private function buildPatch()
 {
     $diff = new DifferentialDiff();
     $diff->attachChangesets($this->getChangesets());
     // TODO: We could batch this to improve performance.
     foreach ($diff->getChangesets() as $changeset) {
         $changeset->attachHunks($changeset->loadHunks());
     }
     $diff_dict = $diff->getDiffDict();
     $changes = array();
     foreach ($diff_dict['changes'] as $changedict) {
         $changes[] = ArcanistDiffChange::newFromDictionary($changedict);
     }
     $bundle = ArcanistBundle::newFromChanges($changes);
     $bundle->setLoadFileDataCallback(array($this, 'loadFileByPHID'));
     $format = PhabricatorEnv::getEnvConfig('metamta.differential.patch-format');
     switch ($format) {
         case 'git':
             return $bundle->toGitPatch();
             break;
         case 'unified':
         default:
             return $bundle->toUnifiedDiff();
             break;
     }
 }
 private function normalizeDiff($text)
 {
     $changes = id(new ArcanistDiffParser())->parseDiff($text);
     ksort($changes);
     return ArcanistBundle::newFromChanges($changes)->toGitPatch();
 }