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;
     }
 }
 /**
  * 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();
 }
 private static function buildChangesetsFromRawChanges(DifferentialDiff $diff, array $changes)
 {
     // There may not be any changes; initialize the changesets list so that
     // we don't throw later when accessing it.
     $diff->attachChangesets(array());
     $lines = 0;
     foreach ($changes as $change) {
         if ($change->getType() == ArcanistDiffChangeType::TYPE_MESSAGE) {
             // If a user pastes a diff into Differential which includes a commit
             // message (e.g., they ran `git show` to generate it), discard that
             // change when constructing a DifferentialDiff.
             continue;
         }
         $changeset = new DifferentialChangeset();
         $add_lines = 0;
         $del_lines = 0;
         $first_line = PHP_INT_MAX;
         $hunks = $change->getHunks();
         if ($hunks) {
             foreach ($hunks as $hunk) {
                 $dhunk = new DifferentialModernHunk();
                 $dhunk->setOldOffset($hunk->getOldOffset());
                 $dhunk->setOldLen($hunk->getOldLength());
                 $dhunk->setNewOffset($hunk->getNewOffset());
                 $dhunk->setNewLen($hunk->getNewLength());
                 $dhunk->setChanges($hunk->getCorpus());
                 $changeset->addUnsavedHunk($dhunk);
                 $add_lines += $hunk->getAddLines();
                 $del_lines += $hunk->getDelLines();
                 $added_lines = $hunk->getChangedLines('new');
                 if ($added_lines) {
                     $first_line = min($first_line, head_key($added_lines));
                 }
             }
             $lines += $add_lines + $del_lines;
         } else {
             // This happens when you add empty files.
             $changeset->attachHunks(array());
         }
         $metadata = $change->getAllMetadata();
         if ($first_line != PHP_INT_MAX) {
             $metadata['line:first'] = $first_line;
         }
         $changeset->setOldFile($change->getOldPath());
         $changeset->setFilename($change->getCurrentPath());
         $changeset->setChangeType($change->getType());
         $changeset->setFileType($change->getFileType());
         $changeset->setMetadata($metadata);
         $changeset->setOldProperties($change->getOldProperties());
         $changeset->setNewProperties($change->getNewProperties());
         $changeset->setAwayPaths($change->getAwayPaths());
         $changeset->setAddLines($add_lines);
         $changeset->setDelLines($del_lines);
         $diff->addUnsavedChangeset($changeset);
     }
     $diff->setLineCount($lines);
     $parser = new DifferentialChangesetParser();
     $changesets = $parser->detectCopiedCode($diff->getChangesets(), $min_width = 30, $min_lines = 3);
     $diff->attachChangesets($changesets);
     return $diff;
 }
 /**
  * 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());
 }
 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;
     }
 }