Skip to content
This repository was archived by the owner on Apr 7, 2022. It is now read-only.

Commit 0db86cc

Browse files
author
epriestley
committed
Improve Diffusion behavior for no-longer-existing commits
Summary: Ref T9028. When users push a commit, then later delete it (e.g., by deleting the branch which contained it) we currently explode when trying to view it. Instead, degrade gradually if some information is not available. Test Plan: - Looked at valid commits with parents, refs, branches and merges. - Looked at invalid commits. - Looked at a previously valid, now-deleted + gc'd commit: {F859273} Reviewers: chad Reviewed By: chad Maniphest Tasks: T9028 Differential Revision: https://secure.phabricator.com/D14227
1 parent 14d6325 commit 0db86cc

File tree

1 file changed

+188
-89
lines changed

1 file changed

+188
-89
lines changed

src/applications/diffusion/controller/DiffusionCommitController.php

Lines changed: 188 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ final class DiffusionCommitController extends DiffusionController {
77
private $auditAuthorityPHIDs;
88
private $highlightedAudits;
99

10+
private $commitParents;
11+
private $commitRefs;
12+
private $commitMerges;
13+
private $commitErrors;
14+
private $commitExists;
15+
1016
public function shouldAllowPublic() {
1117
return true;
1218
}
@@ -17,6 +23,7 @@ protected function shouldLoadDiffusionRequest() {
1723

1824
protected function processDiffusionRequest(AphrontRequest $request) {
1925
$user = $request->getUser();
26+
2027
// This controller doesn't use blob/path stuff, just pass the dictionary
2128
// in directly instead of using the AphrontRequest parsing mechanism.
2229
$data = $request->getURIMap();
@@ -45,10 +52,7 @@ protected function processDiffusionRequest(AphrontRequest $request) {
4552
));
4653

4754
if (!$commit) {
48-
$exists = $this->callConduitWithDiffusionRequest(
49-
'diffusion.existsquery',
50-
array('commit' => $drequest->getCommit()));
51-
if (!$exists) {
55+
if (!$this->getCommitExists()) {
5256
return new Aphront404Response();
5357
}
5458

@@ -95,18 +99,6 @@ protected function processDiffusionRequest(AphrontRequest $request) {
9599

96100
require_celerity_resource('phabricator-remarkup-css');
97101

98-
$parents = $this->callConduitWithDiffusionRequest(
99-
'diffusion.commitparentsquery',
100-
array('commit' => $drequest->getCommit()));
101-
102-
if ($parents) {
103-
$parents = id(new DiffusionCommitQuery())
104-
->setViewer($user)
105-
->withRepository($repository)
106-
->withIdentifiers($parents)
107-
->execute();
108-
}
109-
110102
$headsup_view = id(new PHUIHeaderView())
111103
->setHeader(nonempty($commit->getSummary(), pht('Commit Detail')));
112104

@@ -115,7 +107,6 @@ protected function processDiffusionRequest(AphrontRequest $request) {
115107
$commit_properties = $this->loadCommitProperties(
116108
$commit,
117109
$commit_data,
118-
$parents,
119110
$audit_requests);
120111
$property_list = id(new PHUIPropertyListView())
121112
->setHasKeyboardShortcuts(true)
@@ -148,8 +139,10 @@ protected function processDiffusionRequest(AphrontRequest $request) {
148139
$message));
149140

150141
$headsup_view->setTall(true);
142+
151143
$object_box = id(new PHUIObjectBoxView())
152144
->setHeader($headsup_view)
145+
->setFormErrors($this->getCommitErrors())
153146
->addPropertyList($property_list)
154147
->addPropertyList($detail_list);
155148

@@ -216,6 +209,10 @@ protected function processDiffusionRequest(AphrontRequest $request) {
216209
'This commit is enormous, and affects more than %d files. '.
217210
'Changes are not shown.',
218211
$hard_limit));
212+
} else if (!$this->getCommitExists()) {
213+
$content[] = $this->renderStatusMessage(
214+
pht('Commit No Longer Exists'),
215+
pht('This commit no longer exists in the repository.'));
219216
} else {
220217
$show_changesets = true;
221218

@@ -382,10 +379,8 @@ protected function processDiffusionRequest(AphrontRequest $request) {
382379
private function loadCommitProperties(
383380
PhabricatorRepositoryCommit $commit,
384381
PhabricatorRepositoryCommitData $data,
385-
array $parents,
386382
array $audit_requests) {
387383

388-
assert_instances_of($parents, 'PhabricatorRepositoryCommit');
389384
$viewer = $this->getRequest()->getUser();
390385
$commit_phid = $commit->getPHID();
391386
$drequest = $this->getDiffusionRequest();
@@ -423,11 +418,6 @@ private function loadCommitProperties(
423418
if ($data->getCommitDetail('committerPHID')) {
424419
$phids[] = $data->getCommitDetail('committerPHID');
425420
}
426-
if ($parents) {
427-
foreach ($parents as $parent) {
428-
$phids[] = $parent->getPHID();
429-
}
430-
}
431421

432422
// NOTE: We should never normally have more than a single push log, but
433423
// it can occur naturally if a commit is pushed, then the branch it was
@@ -564,39 +554,47 @@ private function loadCommitProperties(
564554
$props['Differential Revision'] = $handles[$revision_phid]->renderLink();
565555
}
566556

557+
$parents = $this->getCommitParents();
567558
if ($parents) {
568-
$parent_links = array();
569-
foreach ($parents as $parent) {
570-
$parent_links[] = $handles[$parent->getPHID()]->renderLink();
571-
}
572-
$props['Parents'] = phutil_implode_html(" \xC2\xB7 ", $parent_links);
559+
$props['Parents'] = $viewer->renderHandleList(mpull($parents, 'getPHID'));
573560
}
574561

575-
$props['Branches'] = phutil_tag(
576-
'span',
577-
array(
578-
'id' => 'commit-branches',
579-
),
580-
pht('Unknown'));
581-
$props['Tags'] = phutil_tag(
582-
'span',
583-
array(
584-
'id' => 'commit-tags',
585-
),
586-
pht('Unknown'));
562+
if ($this->getCommitExists()) {
563+
$props['Branches'] = phutil_tag(
564+
'span',
565+
array(
566+
'id' => 'commit-branches',
567+
),
568+
pht('Unknown'));
569+
$props['Tags'] = phutil_tag(
570+
'span',
571+
array(
572+
'id' => 'commit-tags',
573+
),
574+
pht('Unknown'));
587575

588-
$callsign = $repository->getCallsign();
589-
$root = '/diffusion/'.$callsign.'/commit/'.$commit->getCommitIdentifier();
590-
Javelin::initBehavior(
591-
'diffusion-commit-branches',
592-
array(
593-
$root.'/branches/' => 'commit-branches',
594-
$root.'/tags/' => 'commit-tags',
595-
));
576+
$callsign = $repository->getCallsign();
577+
$root = '/diffusion/'.$callsign.'/commit/'.$commit->getCommitIdentifier();
578+
Javelin::initBehavior(
579+
'diffusion-commit-branches',
580+
array(
581+
$root.'/branches/' => 'commit-branches',
582+
$root.'/tags/' => 'commit-tags',
583+
));
584+
}
596585

597-
$refs = $this->buildRefs($drequest);
586+
$refs = $this->getCommitRefs();
598587
if ($refs) {
599-
$props['References'] = $refs;
588+
$ref_links = array();
589+
foreach ($refs as $ref_data) {
590+
$ref_links[] = phutil_tag(
591+
'a',
592+
array(
593+
'href' => $ref_data['href'],
594+
),
595+
$ref_data['ref']);
596+
}
597+
$props['References'] = phutil_implode_html(', ', $ref_links);
600598
}
601599

602600
if ($reverts_phids) {
@@ -860,26 +858,12 @@ private function buildMergesTable(PhabricatorRepositoryCommit $commit) {
860858
$drequest = $this->getDiffusionRequest();
861859
$repository = $drequest->getRepository();
862860

863-
$vcs = $repository->getVersionControlSystem();
864-
switch ($vcs) {
865-
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
866-
// These aren't supported under SVN.
867-
return null;
868-
}
869-
870-
$limit = 50;
871-
872-
$merges = $this->callConduitWithDiffusionRequest(
873-
'diffusion.mergedcommitsquery',
874-
array(
875-
'commit' => $drequest->getCommit(),
876-
'limit' => $limit + 1,
877-
));
878-
861+
$merges = $this->getCommitMerges();
879862
if (!$merges) {
880863
return null;
881864
}
882-
$merges = DiffusionPathChange::newFromConduit($merges);
865+
866+
$limit = $this->getMergeDisplayLimit();
883867

884868
$caption = null;
885869
if (count($merges) > $limit) {
@@ -961,27 +945,6 @@ private function renderHeadsupActionList(
961945
return $actions;
962946
}
963947

964-
private function buildRefs(DiffusionRequest $request) {
965-
// this is git-only, so save a conduit round trip and just get out of
966-
// here if the repository isn't git
967-
$type_git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
968-
$repository = $request->getRepository();
969-
if ($repository->getVersionControlSystem() != $type_git) {
970-
return null;
971-
}
972-
973-
$results = $this->callConduitWithDiffusionRequest(
974-
'diffusion.refsquery',
975-
array('commit' => $request->getCommit()));
976-
$ref_links = array();
977-
foreach ($results as $ref_data) {
978-
$ref_links[] = phutil_tag('a',
979-
array('href' => $ref_data['href']),
980-
$ref_data['ref']);
981-
}
982-
return phutil_implode_html(', ', $ref_links);
983-
}
984-
985948
private function buildRawDiffResponse(DiffusionRequest $drequest) {
986949
$raw_diff = $this->callConduitWithDiffusionRequest(
987950
'diffusion.rawdiffquery',
@@ -1137,4 +1100,140 @@ private function buildTableOfContents(array $changesets) {
11371100
return $toc_view;
11381101
}
11391102

1103+
private function loadCommitState() {
1104+
$viewer = $this->getViewer();
1105+
$drequest = $this->getDiffusionRequest();
1106+
$repository = $drequest->getRepository();
1107+
$commit = $drequest->getCommit();
1108+
1109+
// TODO: We could use futures here and resolve these calls in parallel.
1110+
1111+
$exceptions = array();
1112+
1113+
try {
1114+
$parent_refs = $this->callConduitWithDiffusionRequest(
1115+
'diffusion.commitparentsquery',
1116+
array(
1117+
'commit' => $commit,
1118+
));
1119+
1120+
if ($parent_refs) {
1121+
$parents = id(new DiffusionCommitQuery())
1122+
->setViewer($viewer)
1123+
->withRepository($repository)
1124+
->withIdentifiers($parent_refs)
1125+
->execute();
1126+
} else {
1127+
$parents = array();
1128+
}
1129+
1130+
$this->commitParents = $parents;
1131+
} catch (Exception $ex) {
1132+
$this->commitParents = false;
1133+
$exceptions[] = $ex;
1134+
}
1135+
1136+
$merge_limit = $this->getMergeDisplayLimit();
1137+
1138+
try {
1139+
$merges = $this->callConduitWithDiffusionRequest(
1140+
'diffusion.mergedcommitsquery',
1141+
array(
1142+
'commit' => $commit,
1143+
'limit' => $merge_limit + 1,
1144+
));
1145+
1146+
$this->commitMerges = DiffusionPathChange::newFromConduit($merges);
1147+
} catch (Exception $ex) {
1148+
$this->commitMerges = false;
1149+
$exceptions[] = $ex;
1150+
}
1151+
1152+
1153+
try {
1154+
if ($repository->isGit()) {
1155+
$refs = $this->callConduitWithDiffusionRequest(
1156+
'diffusion.refsquery',
1157+
array(
1158+
'commit' => $commit,
1159+
));
1160+
} else {
1161+
$refs = array();
1162+
}
1163+
1164+
$this->commitRefs = $refs;
1165+
} catch (Exception $ex) {
1166+
$this->commitRefs = false;
1167+
$exceptions[] = $ex;
1168+
}
1169+
1170+
if ($exceptions) {
1171+
$exists = $this->callConduitWithDiffusionRequest(
1172+
'diffusion.existsquery',
1173+
array(
1174+
'commit' => $commit,
1175+
));
1176+
1177+
if ($exists) {
1178+
$this->commitExists = true;
1179+
foreach ($exceptions as $exception) {
1180+
$this->commitErrors[] = $exception->getMessage();
1181+
}
1182+
} else {
1183+
$this->commitExists = false;
1184+
$this->commitErrors[] = pht(
1185+
'This commit no longer exists in the repository. It may have '.
1186+
'been part of a branch which was deleted.');
1187+
}
1188+
} else {
1189+
$this->commitExists = true;
1190+
$this->commitErrors = array();
1191+
}
1192+
}
1193+
1194+
private function getMergeDisplayLimit() {
1195+
return 50;
1196+
}
1197+
1198+
private function getCommitExists() {
1199+
if ($this->commitExists === null) {
1200+
$this->loadCommitState();
1201+
}
1202+
1203+
return $this->commitExists;
1204+
}
1205+
1206+
private function getCommitParents() {
1207+
if ($this->commitParents === null) {
1208+
$this->loadCommitState();
1209+
}
1210+
1211+
return $this->commitParents;
1212+
}
1213+
1214+
private function getCommitRefs() {
1215+
if ($this->commitRefs === null) {
1216+
$this->loadCommitState();
1217+
}
1218+
1219+
return $this->commitRefs;
1220+
}
1221+
1222+
private function getCommitMerges() {
1223+
if ($this->commitMerges === null) {
1224+
$this->loadCommitState();
1225+
}
1226+
1227+
return $this->commitMerges;
1228+
}
1229+
1230+
private function getCommitErrors() {
1231+
if ($this->commitErrors === null) {
1232+
$this->loadCommitState();
1233+
}
1234+
1235+
return $this->commitErrors;
1236+
}
1237+
1238+
11401239
}

0 commit comments

Comments
 (0)