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:
parent
1bd0a1f0d9
commit
38e029205b
1 changed files with 174 additions and 26 deletions
|
@ -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());
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue