mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-26 08:42:41 +01:00
When showing a diff-of-diffs, hide files which didn't get any more changes and have no inlines
Summary: Ref T13137. See that task for discussion. When we show a diff-of-diffs, we often render stubs for files which didn't change between the diffs. These stubs usually aren't a big deal, but for certain types of changes (like refactors) they can create a lot of clutter. Instead, hide these stubs and show a notice that we hid them. Test Plan: - Created a revision affecting 4 files. - Updated it with a diff that changed only 1 of the 4 files. - Added an inline comment to a different file. - Viewed the diff of diffs. - Before: 4 changesets with two "nothing changed" stubs. - After: 2 changesets with the stubs hidden. {F5621083} Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13137 Differential Revision: https://secure.phabricator.com/D19453
This commit is contained in:
parent
79fdf5c127
commit
8f9b948447
4 changed files with 153 additions and 20 deletions
|
@ -5,6 +5,7 @@ final class DifferentialRevisionViewController
|
||||||
|
|
||||||
private $revisionID;
|
private $revisionID;
|
||||||
private $changesetCount;
|
private $changesetCount;
|
||||||
|
private $hiddenChangesets;
|
||||||
|
|
||||||
public function shouldAllowPublic() {
|
public function shouldAllowPublic() {
|
||||||
return true;
|
return true;
|
||||||
|
@ -169,6 +170,7 @@ final class DifferentialRevisionViewController
|
||||||
}
|
}
|
||||||
|
|
||||||
$handles = $this->loadViewerHandles($object_phids);
|
$handles = $this->loadViewerHandles($object_phids);
|
||||||
|
$warnings = array();
|
||||||
|
|
||||||
$request_uri = $request->getRequestURI();
|
$request_uri = $request->getRequestURI();
|
||||||
|
|
||||||
|
@ -203,11 +205,19 @@ final class DifferentialRevisionViewController
|
||||||
pht('Expand All Files'))),
|
pht('Expand All Files'))),
|
||||||
);
|
);
|
||||||
|
|
||||||
$warning = id(new PHUIInfoView())
|
$warnings[] = id(new PHUIInfoView())
|
||||||
->setTitle(pht('Large Diff'))
|
->setTitle(pht('Large Diff'))
|
||||||
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
|
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
|
||||||
->appendChild($message);
|
->appendChild($message);
|
||||||
|
|
||||||
|
$folded_changesets = $changesets;
|
||||||
|
} else {
|
||||||
|
$folded_changesets = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't hide or fold changesets which have inline comments.
|
||||||
|
$hidden_changesets = $this->hiddenChangesets;
|
||||||
|
if ($hidden_changesets || $folded_changesets) {
|
||||||
$old = array_select_keys($changesets, $old_ids);
|
$old = array_select_keys($changesets, $old_ids);
|
||||||
$new = array_select_keys($changesets, $new_ids);
|
$new = array_select_keys($changesets, $new_ids);
|
||||||
|
|
||||||
|
@ -222,16 +232,47 @@ final class DifferentialRevisionViewController
|
||||||
$new,
|
$new,
|
||||||
$revision);
|
$revision);
|
||||||
|
|
||||||
$visible_changesets = array();
|
|
||||||
foreach ($inlines as $inline) {
|
foreach ($inlines as $inline) {
|
||||||
$changeset_id = $inline->getChangesetID();
|
$changeset_id = $inline->getChangesetID();
|
||||||
if (isset($changesets[$changeset_id])) {
|
if (!isset($changesets[$changeset_id])) {
|
||||||
$visible_changesets[$changeset_id] = $changesets[$changeset_id];
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($hidden_changesets[$changeset_id]);
|
||||||
|
unset($folded_changesets[$changeset_id]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
$warning = null;
|
// If we would hide only one changeset, don't hide anything. The notice
|
||||||
$visible_changesets = $changesets;
|
// we'd render about it is about the same size as the changeset.
|
||||||
|
if (count($hidden_changesets) < 2) {
|
||||||
|
$hidden_changesets = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the set of hidden changesets, since we may have just un-hidden
|
||||||
|
// some of them.
|
||||||
|
if ($hidden_changesets) {
|
||||||
|
$warnings[] = id(new PHUIInfoView())
|
||||||
|
->setTitle(pht('Showing Only Differences'))
|
||||||
|
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
|
||||||
|
->appendChild(
|
||||||
|
pht(
|
||||||
|
'This revision modifies %s more files that are hidden because '.
|
||||||
|
'they were not modified between selected diffs and they have no '.
|
||||||
|
'inline comments.',
|
||||||
|
phutil_count($hidden_changesets)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the unfolded changesets. By default, everything is unfolded.
|
||||||
|
$unfolded_changesets = $changesets;
|
||||||
|
foreach ($folded_changesets as $changeset_id => $changeset) {
|
||||||
|
unset($unfolded_changesets[$changeset_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Throw away any hidden changesets.
|
||||||
|
foreach ($hidden_changesets as $changeset_id => $changeset) {
|
||||||
|
unset($changesets[$changeset_id]);
|
||||||
|
unset($unfolded_changesets[$changeset_id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$commit_hashes = mpull($diffs, 'getSourceControlBaseRevision');
|
$commit_hashes = mpull($diffs, 'getSourceControlBaseRevision');
|
||||||
|
@ -267,7 +308,7 @@ final class DifferentialRevisionViewController
|
||||||
if ($repository) {
|
if ($repository) {
|
||||||
$symbol_indexes = $this->buildSymbolIndexes(
|
$symbol_indexes = $this->buildSymbolIndexes(
|
||||||
$repository,
|
$repository,
|
||||||
$visible_changesets);
|
$unfolded_changesets);
|
||||||
} else {
|
} else {
|
||||||
$symbol_indexes = array();
|
$symbol_indexes = array();
|
||||||
}
|
}
|
||||||
|
@ -328,7 +369,7 @@ final class DifferentialRevisionViewController
|
||||||
} else {
|
} else {
|
||||||
$changeset_view = id(new DifferentialChangesetListView())
|
$changeset_view = id(new DifferentialChangesetListView())
|
||||||
->setChangesets($changesets)
|
->setChangesets($changesets)
|
||||||
->setVisibleChangesets($visible_changesets)
|
->setVisibleChangesets($unfolded_changesets)
|
||||||
->setStandaloneURI('/differential/changeset/')
|
->setStandaloneURI('/differential/changeset/')
|
||||||
->setRawFileURIs(
|
->setRawFileURIs(
|
||||||
'/differential/changeset/?view=old',
|
'/differential/changeset/?view=old',
|
||||||
|
@ -405,7 +446,7 @@ final class DifferentialRevisionViewController
|
||||||
|
|
||||||
$toc_view = $this->buildTableOfContents(
|
$toc_view = $this->buildTableOfContents(
|
||||||
$changesets,
|
$changesets,
|
||||||
$visible_changesets,
|
$unfolded_changesets,
|
||||||
$target->loadCoverageMap($viewer));
|
$target->loadCoverageMap($viewer));
|
||||||
|
|
||||||
// Attach changesets to each reviewer so we can show which Owners package
|
// Attach changesets to each reviewer so we can show which Owners package
|
||||||
|
@ -520,7 +561,7 @@ final class DifferentialRevisionViewController
|
||||||
|
|
||||||
$footer[] = array(
|
$footer[] = array(
|
||||||
$anchor,
|
$anchor,
|
||||||
$warning,
|
$warnings,
|
||||||
$tab_view,
|
$tab_view,
|
||||||
$changeset_view,
|
$changeset_view,
|
||||||
);
|
);
|
||||||
|
@ -807,6 +848,7 @@ final class DifferentialRevisionViewController
|
||||||
DifferentialDiff $target,
|
DifferentialDiff $target,
|
||||||
DifferentialDiff $diff_vs = null,
|
DifferentialDiff $diff_vs = null,
|
||||||
PhabricatorRepository $repository = null) {
|
PhabricatorRepository $repository = null) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
$load_diffs = array($target);
|
$load_diffs = array($target);
|
||||||
if ($diff_vs) {
|
if ($diff_vs) {
|
||||||
|
@ -814,7 +856,7 @@ final class DifferentialRevisionViewController
|
||||||
}
|
}
|
||||||
|
|
||||||
$raw_changesets = id(new DifferentialChangesetQuery())
|
$raw_changesets = id(new DifferentialChangesetQuery())
|
||||||
->setViewer($this->getRequest()->getUser())
|
->setViewer($viewer)
|
||||||
->withDiffs($load_diffs)
|
->withDiffs($load_diffs)
|
||||||
->execute();
|
->execute();
|
||||||
$changeset_groups = mgroup($raw_changesets, 'getDiffID');
|
$changeset_groups = mgroup($raw_changesets, 'getDiffID');
|
||||||
|
@ -825,6 +867,7 @@ final class DifferentialRevisionViewController
|
||||||
$refs = array();
|
$refs = array();
|
||||||
$vs_map = array();
|
$vs_map = array();
|
||||||
$vs_changesets = array();
|
$vs_changesets = array();
|
||||||
|
$must_compare = array();
|
||||||
if ($diff_vs) {
|
if ($diff_vs) {
|
||||||
$vs_id = $diff_vs->getID();
|
$vs_id = $diff_vs->getID();
|
||||||
$vs_changesets_path_map = array();
|
$vs_changesets_path_map = array();
|
||||||
|
@ -833,6 +876,7 @@ final class DifferentialRevisionViewController
|
||||||
$vs_changesets_path_map[$path] = $changeset;
|
$vs_changesets_path_map[$path] = $changeset;
|
||||||
$vs_changesets[$changeset->getID()] = $changeset;
|
$vs_changesets[$changeset->getID()] = $changeset;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($changesets as $key => $changeset) {
|
foreach ($changesets as $key => $changeset) {
|
||||||
$path = $changeset->getAbsoluteRepositoryPath($repository, $target);
|
$path = $changeset->getAbsoluteRepositoryPath($repository, $target);
|
||||||
if (isset($vs_changesets_path_map[$path])) {
|
if (isset($vs_changesets_path_map[$path])) {
|
||||||
|
@ -841,15 +885,20 @@ final class DifferentialRevisionViewController
|
||||||
$refs[$changeset->getID()] =
|
$refs[$changeset->getID()] =
|
||||||
$changeset->getID().'/'.$vs_changesets_path_map[$path]->getID();
|
$changeset->getID().'/'.$vs_changesets_path_map[$path]->getID();
|
||||||
unset($vs_changesets_path_map[$path]);
|
unset($vs_changesets_path_map[$path]);
|
||||||
|
|
||||||
|
$must_compare[] = $changeset->getID();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$refs[$changeset->getID()] = $changeset->getID();
|
$refs[$changeset->getID()] = $changeset->getID();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($vs_changesets_path_map as $path => $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 {
|
} else {
|
||||||
foreach ($changesets as $changeset) {
|
foreach ($changesets as $changeset) {
|
||||||
$refs[$changeset->getID()] = $changeset->getID();
|
$refs[$changeset->getID()] = $changeset->getID();
|
||||||
|
@ -858,13 +907,25 @@ final class DifferentialRevisionViewController
|
||||||
|
|
||||||
$changesets = msort($changesets, 'getSortKey');
|
$changesets = msort($changesets, 'getSortKey');
|
||||||
|
|
||||||
|
// See T13137. When displaying the diff between two updates, hide any
|
||||||
|
// changesets which haven't actually changed.
|
||||||
|
$this->hiddenChangesets = array();
|
||||||
|
foreach ($must_compare as $changeset_id) {
|
||||||
|
$changeset = $changesets[$changeset_id];
|
||||||
|
$vs_changeset = $vs_changesets[$vs_map[$changeset_id]];
|
||||||
|
|
||||||
|
if ($changeset->hasSameEffectAs($vs_changeset)) {
|
||||||
|
$this->hiddenChangesets[$changeset_id] = $changesets[$changeset_id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return array($changesets, $vs_map, $vs_changesets, $refs);
|
return array($changesets, $vs_map, $vs_changesets, $refs);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildSymbolIndexes(
|
private function buildSymbolIndexes(
|
||||||
PhabricatorRepository $repository,
|
PhabricatorRepository $repository,
|
||||||
array $visible_changesets) {
|
array $unfolded_changesets) {
|
||||||
assert_instances_of($visible_changesets, 'DifferentialChangeset');
|
assert_instances_of($unfolded_changesets, 'DifferentialChangeset');
|
||||||
|
|
||||||
$engine = PhabricatorSyntaxHighlighter::newEngine();
|
$engine = PhabricatorSyntaxHighlighter::newEngine();
|
||||||
|
|
||||||
|
@ -889,7 +950,7 @@ final class DifferentialRevisionViewController
|
||||||
$sources);
|
$sources);
|
||||||
|
|
||||||
$indexed_langs = array_fill_keys($langs, true);
|
$indexed_langs = array_fill_keys($langs, true);
|
||||||
foreach ($visible_changesets as $key => $changeset) {
|
foreach ($unfolded_changesets as $key => $changeset) {
|
||||||
$lang = $engine->getLanguageFromFilename($changeset->getFilename());
|
$lang = $engine->getLanguageFromFilename($changeset->getFilename());
|
||||||
if (empty($indexed_langs) || isset($indexed_langs[$lang])) {
|
if (empty($indexed_langs) || isset($indexed_langs[$lang])) {
|
||||||
$symbol_indexes[$key] = array(
|
$symbol_indexes[$key] = array(
|
||||||
|
|
|
@ -7,6 +7,7 @@ final class DifferentialChangesetEngine extends Phobject {
|
||||||
|
|
||||||
foreach ($changesets as $changeset) {
|
foreach ($changesets as $changeset) {
|
||||||
$this->detectGeneratedCode($changeset);
|
$this->detectGeneratedCode($changeset);
|
||||||
|
$this->computeHashes($changeset);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->detectCopiedCode($changesets);
|
$this->detectCopiedCode($changesets);
|
||||||
|
@ -59,6 +60,30 @@ final class DifferentialChangesetEngine extends Phobject {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( Content Hashes )----------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
private function computeHashes(DifferentialChangeset $changeset) {
|
||||||
|
|
||||||
|
$effect_key = DifferentialChangeset::METADATA_EFFECT_HASH;
|
||||||
|
|
||||||
|
$effect_hash = $this->newEffectHash($changeset);
|
||||||
|
if ($effect_hash !== null) {
|
||||||
|
$changeset->setChangesetMetadata($effect_key, $effect_hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function newEffectHash(DifferentialChangeset $changeset) {
|
||||||
|
|
||||||
|
if ($changeset->getHunks()) {
|
||||||
|
$new_data = $changeset->makeNewFile();
|
||||||
|
return PhabricatorHash::digestForIndex($new_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( Copied Code )-------------------------------------------------------- */
|
/* -( Copied Code )-------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -82,6 +82,10 @@ final class PhabricatorDifferentialRebuildChangesetsWorkflow
|
||||||
id(new DifferentialChangesetEngine())
|
id(new DifferentialChangesetEngine())
|
||||||
->rebuildChangesets($changesets);
|
->rebuildChangesets($changesets);
|
||||||
|
|
||||||
|
foreach ($changesets as $changeset) {
|
||||||
|
$changeset->save();
|
||||||
|
}
|
||||||
|
|
||||||
echo tsprintf(
|
echo tsprintf(
|
||||||
"%s\n",
|
"%s\n",
|
||||||
pht('Done.'));
|
pht('Done.'));
|
||||||
|
|
|
@ -26,6 +26,7 @@ final class DifferentialChangeset
|
||||||
|
|
||||||
const METADATA_TRUSTED_ATTRIBUTES = 'attributes.trusted';
|
const METADATA_TRUSTED_ATTRIBUTES = 'attributes.trusted';
|
||||||
const METADATA_UNTRUSTED_ATTRIBUTES = 'attributes.untrusted';
|
const METADATA_UNTRUSTED_ATTRIBUTES = 'attributes.untrusted';
|
||||||
|
const METADATA_EFFECT_HASH = 'hash.effect';
|
||||||
|
|
||||||
const ATTRIBUTE_GENERATED = 'generated';
|
const ATTRIBUTE_GENERATED = 'generated';
|
||||||
|
|
||||||
|
@ -143,6 +144,48 @@ final class DifferentialChangeset
|
||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test if this changeset and some other changeset put the affected file in
|
||||||
|
* the same state.
|
||||||
|
*
|
||||||
|
* @param DifferentialChangeset Changeset to compare against.
|
||||||
|
* @return bool True if the two changesets have the same effect.
|
||||||
|
*/
|
||||||
|
public function hasSameEffectAs(DifferentialChangeset $other) {
|
||||||
|
if ($this->getFilename() !== $other->getFilename()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$hash_key = self::METADATA_EFFECT_HASH;
|
||||||
|
|
||||||
|
$u_hash = $this->getChangesetMetadata($hash_key);
|
||||||
|
if ($u_hash === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$v_hash = $other->getChangesetMetadata($hash_key);
|
||||||
|
if ($v_hash === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($u_hash !== $v_hash) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the final states for the file properties (like the "+x"
|
||||||
|
// executable bit) match one another.
|
||||||
|
$u_props = $this->getNewProperties();
|
||||||
|
$v_props = $other->getNewProperties();
|
||||||
|
ksort($u_props);
|
||||||
|
ksort($v_props);
|
||||||
|
|
||||||
|
if ($u_props !== $v_props) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public function getSortKey() {
|
public function getSortKey() {
|
||||||
$sort_key = $this->getFilename();
|
$sort_key = $this->getFilename();
|
||||||
// Sort files with ".h" in them first, so headers (.h, .hpp) come before
|
// Sort files with ".h" in them first, so headers (.h, .hpp) come before
|
||||||
|
|
Loading…
Reference in a new issue