1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-23 07:12:41 +01:00

Add a "Download Raw Diff" action to Differential

Summary: this lets people download diffs easily in case arc export, arc patch, etc isn't to their liking or whatever.

Test Plan: "Download Raw Diff" a few times with various things toggled in the "Revision Update History" widget

Reviewers: epriestley

Reviewed By: epriestley

CC: aran, Korvin

Maniphest Tasks: T1294

Differential Revision: https://secure.phabricator.com/D2855
This commit is contained in:
Bob Trahan 2012-06-28 10:12:20 -07:00
parent 1bd0a1f0d9
commit 38e029205b

View file

@ -66,6 +66,22 @@ final class DifferentialRevisionViewController extends DifferentialController {
$diff_vs = null; $diff_vs = null;
} }
$arc_project = $target->loadArcanistProject();
$repository = ($arc_project ? $arc_project->loadRepository() : null);
list($changesets, $vs_map, $vs_changesets, $rendering_references) =
$this->loadChangesetsAndVsMap(
$target,
idx($diffs, $diff_vs),
$repository);
if ($request->getExists('download')) {
return $this->buildRawDiffResponse($changesets,
$vs_changesets,
$vs_map,
$repository);
}
list($aux_fields, $props) = $this->loadAuxiliaryFieldsAndProperties( list($aux_fields, $props) = $this->loadAuxiliaryFieldsAndProperties(
$revision, $revision,
$target_manual, $target_manual,
@ -75,14 +91,6 @@ final class DifferentialRevisionViewController extends DifferentialController {
'arc:unit', 'arc:unit',
)); ));
$arc_project = $target->loadArcanistProject();
$repository = ($arc_project ? $arc_project->loadRepository() : null);
list($changesets, $vs_map, $rendering_references) =
$this->loadChangesetsAndVsMap(
$target,
idx($diffs, $diff_vs),
$repository);
$comments = $revision->loadComments(); $comments = $revision->loadComments();
$comments = array_merge( $comments = array_merge(
@ -505,6 +513,13 @@ final class DifferentialRevisionViewController extends DifferentialController {
); );
} }
$request_uri = $this->getRequest()->getRequestURI();
$links[] = array(
'class' => 'action-download',
'name' => 'Download Raw Diff',
'href' => $request_uri->alter('download', 'true')
);
return $links; return $links;
} }
@ -642,40 +657,43 @@ final class DifferentialRevisionViewController extends DifferentialController {
$changesets = idx($changeset_groups, $target->getID(), array()); $changesets = idx($changeset_groups, $target->getID(), array());
$changesets = mpull($changesets, null, 'getID'); $changesets = mpull($changesets, null, 'getID');
$refs = array(); $refs = array();
foreach ($changesets as $changeset) { $vs_map = array();
$refs[$changeset->getID()] = $changeset->getID(); $vs_changesets = array();
}
$vs_map = array();
if ($diff_vs) { if ($diff_vs) {
$vs_changesets = array(); $vs_id = $diff_vs->getID();
$vs_id = $diff_vs->getID(); $vs_changesets_path_map = array();
foreach (idx($changeset_groups, $vs_id, array()) as $changeset) { foreach (idx($changeset_groups, $vs_id, array()) as $changeset) {
$path = $changeset->getAbsoluteRepositoryPath($repository, $diff_vs); $path = $changeset->getAbsoluteRepositoryPath($repository, $diff_vs);
$vs_changesets[$path] = $changeset; $vs_changesets_path_map[$path] = $changeset;
$vs_changesets[$changeset->getID()] = $changeset;
} }
foreach ($changesets as $key => $changeset) { foreach ($changesets as $key => $changeset) {
$file = $changeset->getAbsoluteRepositoryPath($repository, $target); $path = $changeset->getAbsoluteRepositoryPath($repository, $target);
if (isset($vs_changesets[$file])) { if (isset($vs_changesets_path_map[$path])) {
$vs_map[$changeset->getID()] = $vs_changesets[$file]->getID(); $vs_map[$changeset->getID()] =
$vs_changesets_path_map[$path]->getID();
$refs[$changeset->getID()] = $refs[$changeset->getID()] =
$changeset->getID().'/'.$vs_changesets[$file]->getID(); $changeset->getID().'/'.$vs_changesets_path_map[$path]->getID();
unset($vs_changesets[$file]); unset($vs_changesets_path_map[$path]);
} else { } else {
$refs[$changeset->getID()] = $changeset->getID(); $refs[$changeset->getID()] = $changeset->getID();
} }
} }
foreach ($vs_changesets as $changeset) { foreach ($vs_changesets_path_map as $path => $changeset) {
$changesets[$changeset->getID()] = $changeset; $changesets[$changeset->getID()] = $changeset;
$vs_map[$changeset->getID()] = -1; $vs_map[$changeset->getID()] = -1;
$refs[$changeset->getID()] = $changeset->getID().'/-1'; $refs[$changeset->getID()] = $changeset->getID().'/-1';
}
} else {
foreach ($changesets as $changeset) {
$refs[$changeset->getID()] = $changeset->getID();
} }
} }
$changesets = msort($changesets, 'getSortKey'); $changesets = msort($changesets, 'getSortKey');
return array($changesets, $vs_map, $refs); return array($changesets, $vs_map, $vs_changesets, $refs);
} }
private function loadAuxiliaryFieldsAndProperties( private function loadAuxiliaryFieldsAndProperties(
@ -834,4 +852,134 @@ final class DifferentialRevisionViewController extends DifferentialController {
'</div>'; '</div>';
} }
/**
* Straight copy of the loadFileByPhid method in
* @{class:DifferentialReviewRequestMail}.
*
* This is because of the code similarity between the buildPatch method in
* @{class:DifferentialReviewRequestMail} and @{method:buildRawDiffResponse}
* in this class. Both of these methods end up using call_user_func and this
* piece of code is the lucky function.
*
* @return mixed (@{class:PhabricatorFile} if found, null if not)
*/
public function loadFileByPHID($phid) {
$file = id(new PhabricatorFile())->loadOneWhere(
'phid = %s',
$phid);
if (!$file) {
return null;
}
return $file->loadFileData();
}
/**
* 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());
$vs = idx($vs_map, $changeset->getID());
if ($vs) {
$choice = $vs_changeset = $vs_changesets[$vs];
$vs_changeset->attachHunks($vs_changeset->loadHunks());
$right = $vs_changeset->makeNewFile();
} else {
$choice = $changeset;
$right = $changeset->makeOldFile();
}
$left = $changeset->makeNewFile();
$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:
$raw_diff = $bundle->toGitPatch();
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
default:
$raw_diff = $bundle->toUnifiedDiff();
break;
}
$hash = PhabricatorHash::digest($raw_diff);
$file = id(new PhabricatorFile())->loadOneWhere(
'contentHash = %s LIMIT 1',
$hash);
if (!$file) {
$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';
// We're just caching the data; this is always safe.
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$file = PhabricatorFile::newFromFileData(
$raw_diff,
array(
'name' => $file_name,
));
unset($unguarded);
}
return id(new AphrontRedirectResponse())->setURI($file->getBestURI());
}
} }