2016-01-08 13:35:17 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
final class DifferentialDiffExtractionEngine extends Phobject {
|
|
|
|
|
|
|
|
private $viewer;
|
|
|
|
private $authorPHID;
|
|
|
|
|
|
|
|
public function setViewer(PhabricatorUser $viewer) {
|
|
|
|
$this->viewer = $viewer;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getViewer() {
|
|
|
|
return $this->viewer;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setAuthorPHID($author_phid) {
|
|
|
|
$this->authorPHID = $author_phid;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getAuthorPHID() {
|
|
|
|
return $this->authorPHID;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function newDiffFromCommit(PhabricatorRepositoryCommit $commit) {
|
|
|
|
$viewer = $this->getViewer();
|
|
|
|
|
|
|
|
$repository = $commit->getRepository();
|
|
|
|
$identifier = $commit->getCommitIdentifier();
|
|
|
|
$monogram = $commit->getMonogram();
|
|
|
|
|
|
|
|
$drequest = DiffusionRequest::newFromDictionary(
|
|
|
|
array(
|
|
|
|
'user' => $viewer,
|
|
|
|
'repository' => $repository,
|
|
|
|
));
|
|
|
|
|
|
|
|
$raw_diff = DiffusionQuery::callConduitWithDiffusionRequest(
|
|
|
|
$viewer,
|
|
|
|
$drequest,
|
|
|
|
'diffusion.rawdiffquery',
|
|
|
|
array(
|
|
|
|
'commit' => $identifier,
|
|
|
|
));
|
|
|
|
|
|
|
|
// TODO: Support adds, deletes and moves under SVN.
|
|
|
|
if (strlen($raw_diff)) {
|
|
|
|
$changes = id(new ArcanistDiffParser())->parseDiff($raw_diff);
|
|
|
|
} else {
|
|
|
|
// This is an empty diff, maybe made with `git commit --allow-empty`.
|
|
|
|
// NOTE: These diffs have the same tree hash as their ancestors, so
|
|
|
|
// they may attach to revisions in an unexpected way. Just let this
|
|
|
|
// happen for now, although it might make sense to special case it
|
|
|
|
// eventually.
|
|
|
|
$changes = array();
|
|
|
|
}
|
|
|
|
|
|
|
|
$diff = DifferentialDiff::newFromRawChanges($viewer, $changes)
|
|
|
|
->setRepositoryPHID($repository->getPHID())
|
|
|
|
->setCreationMethod('commit')
|
|
|
|
->setSourceControlSystem($repository->getVersionControlSystem())
|
|
|
|
->setLintStatus(DifferentialLintStatus::LINT_AUTO_SKIP)
|
|
|
|
->setUnitStatus(DifferentialUnitStatus::UNIT_AUTO_SKIP)
|
|
|
|
->setDateCreated($commit->getEpoch())
|
|
|
|
->setDescription($monogram);
|
|
|
|
|
|
|
|
$author_phid = $this->getAuthorPHID();
|
|
|
|
if ($author_phid !== null) {
|
|
|
|
$diff->setAuthorPHID($author_phid);
|
|
|
|
}
|
|
|
|
|
|
|
|
$parents = DiffusionQuery::callConduitWithDiffusionRequest(
|
|
|
|
$viewer,
|
|
|
|
$drequest,
|
|
|
|
'diffusion.commitparentsquery',
|
|
|
|
array(
|
|
|
|
'commit' => $identifier,
|
|
|
|
));
|
|
|
|
|
|
|
|
if ($parents) {
|
|
|
|
$diff->setSourceControlBaseRevision(head($parents));
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Attach binary files.
|
|
|
|
|
|
|
|
return $diff->save();
|
|
|
|
}
|
|
|
|
|
2016-01-08 13:59:31 +01:00
|
|
|
public function isDiffChangedBeforeCommit(
|
|
|
|
PhabricatorRepositoryCommit $commit,
|
|
|
|
DifferentialDiff $old,
|
|
|
|
DifferentialDiff $new) {
|
|
|
|
|
|
|
|
$viewer = $this->getViewer();
|
|
|
|
$repository = $commit->getRepository();
|
|
|
|
$identifier = $commit->getCommitIdentifier();
|
|
|
|
|
|
|
|
$vs_changesets = array();
|
|
|
|
foreach ($old->getChangesets() as $changeset) {
|
|
|
|
$path = $changeset->getAbsoluteRepositoryPath($repository, $old);
|
|
|
|
$path = ltrim($path, '/');
|
|
|
|
$vs_changesets[$path] = $changeset;
|
|
|
|
}
|
|
|
|
|
|
|
|
$changesets = array();
|
|
|
|
foreach ($new->getChangesets() as $changeset) {
|
|
|
|
$path = $changeset->getAbsoluteRepositoryPath($repository, $new);
|
|
|
|
$path = ltrim($path, '/');
|
|
|
|
$changesets[$path] = $changeset;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (array_fill_keys(array_keys($changesets), true) !=
|
|
|
|
array_fill_keys(array_keys($vs_changesets), true)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
$file_phids = array();
|
|
|
|
foreach ($vs_changesets as $changeset) {
|
|
|
|
$metadata = $changeset->getMetadata();
|
|
|
|
$file_phid = idx($metadata, 'new:binary-phid');
|
|
|
|
if ($file_phid) {
|
|
|
|
$file_phids[$file_phid] = $file_phid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$files = array();
|
|
|
|
if ($file_phids) {
|
|
|
|
$files = id(new PhabricatorFileQuery())
|
|
|
|
->setViewer(PhabricatorUser::getOmnipotentUser())
|
|
|
|
->withPHIDs($file_phids)
|
|
|
|
->execute();
|
|
|
|
$files = mpull($files, null, 'getPHID');
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($changesets as $path => $changeset) {
|
|
|
|
$vs_changeset = $vs_changesets[$path];
|
|
|
|
|
|
|
|
$file_phid = idx($vs_changeset->getMetadata(), 'new:binary-phid');
|
|
|
|
if ($file_phid) {
|
|
|
|
if (!isset($files[$file_phid])) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-01-08 14:51:58 +01:00
|
|
|
$drequest = DiffusionRequest::newFromDictionary(
|
|
|
|
array(
|
|
|
|
'user' => $viewer,
|
|
|
|
'repository' => $repository,
|
|
|
|
));
|
|
|
|
|
|
|
|
$response = DiffusionQuery::callConduitWithDiffusionRequest(
|
|
|
|
$viewer,
|
|
|
|
$drequest,
|
|
|
|
'diffusion.filecontentquery',
|
|
|
|
array(
|
|
|
|
'commit' => $identifier,
|
|
|
|
'path' => $path,
|
|
|
|
));
|
2016-01-08 13:59:31 +01:00
|
|
|
|
2016-01-07 12:50:16 +01:00
|
|
|
$new_file_phid = $response['filePHID'];
|
|
|
|
if (!$new_file_phid) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
$new_file = id(new PhabricatorFileQuery())
|
|
|
|
->setViewer($viewer)
|
|
|
|
->withPHIDs(array($new_file_phid))
|
|
|
|
->executeOne();
|
|
|
|
if (!$new_file) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($files[$file_phid]->loadFileData() != $new_file->loadFileData()) {
|
2016-01-08 13:59:31 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$context = implode("\n", $changeset->makeChangesWithContext());
|
|
|
|
$vs_context = implode("\n", $vs_changeset->makeChangesWithContext());
|
|
|
|
|
|
|
|
// We couldn't just compare $context and $vs_context because following
|
|
|
|
// diffs will be considered different:
|
|
|
|
//
|
|
|
|
// -(empty line)
|
|
|
|
// -echo 'test';
|
|
|
|
// (empty line)
|
|
|
|
//
|
|
|
|
// (empty line)
|
|
|
|
// -echo "test";
|
|
|
|
// -(empty line)
|
|
|
|
|
|
|
|
$hunk = id(new DifferentialModernHunk())->setChanges($context);
|
|
|
|
$vs_hunk = id(new DifferentialModernHunk())->setChanges($vs_context);
|
|
|
|
if ($hunk->makeOldFile() != $vs_hunk->makeOldFile() ||
|
|
|
|
$hunk->makeNewFile() != $vs_hunk->makeNewFile()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function updateRevisionWithCommit(
|
|
|
|
DifferentialRevision $revision,
|
|
|
|
PhabricatorRepositoryCommit $commit,
|
|
|
|
array $more_xactions,
|
|
|
|
PhabricatorContentSource $content_source) {
|
|
|
|
|
|
|
|
$viewer = $this->getViewer();
|
|
|
|
$result_data = array();
|
|
|
|
|
|
|
|
$new_diff = $this->newDiffFromCommit($commit);
|
|
|
|
|
|
|
|
$old_diff = $revision->getActiveDiff();
|
|
|
|
$changed_uri = null;
|
|
|
|
if ($old_diff) {
|
|
|
|
$old_diff = id(new DifferentialDiffQuery())
|
|
|
|
->setViewer($viewer)
|
|
|
|
->withIDs(array($old_diff->getID()))
|
|
|
|
->needChangesets(true)
|
|
|
|
->executeOne();
|
|
|
|
if ($old_diff) {
|
|
|
|
$has_changed = $this->isDiffChangedBeforeCommit(
|
|
|
|
$commit,
|
|
|
|
$old_diff,
|
|
|
|
$new_diff);
|
|
|
|
if ($has_changed) {
|
|
|
|
$result_data['vsDiff'] = $old_diff->getID();
|
|
|
|
|
|
|
|
$revision_monogram = $revision->getMonogram();
|
|
|
|
$old_id = $old_diff->getID();
|
|
|
|
$new_id = $new_diff->getID();
|
|
|
|
|
|
|
|
$changed_uri = "/{$revision_monogram}?vs={$old_id}&id={$new_id}#toc";
|
|
|
|
$changed_uri = PhabricatorEnv::getProductionURI($changed_uri);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$xactions = array();
|
|
|
|
|
|
|
|
$xactions[] = id(new DifferentialTransaction())
|
|
|
|
->setTransactionType(DifferentialTransaction::TYPE_UPDATE)
|
|
|
|
->setIgnoreOnNoEffect(true)
|
|
|
|
->setNewValue($new_diff->getPHID())
|
|
|
|
->setMetadataValue('isCommitUpdate', true);
|
|
|
|
|
|
|
|
foreach ($more_xactions as $more_xaction) {
|
|
|
|
$xactions[] = $more_xaction;
|
|
|
|
}
|
|
|
|
|
|
|
|
$editor = id(new DifferentialTransactionEditor())
|
|
|
|
->setActor($viewer)
|
|
|
|
->setContinueOnMissingFields(true)
|
|
|
|
->setContentSource($content_source)
|
|
|
|
->setChangedPriorToCommitURI($changed_uri)
|
|
|
|
->setIsCloseByCommit(true);
|
|
|
|
|
|
|
|
$author_phid = $this->getAuthorPHID();
|
|
|
|
if ($author_phid !== null) {
|
|
|
|
$editor->setActingAsPHID($author_phid);
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
$editor->applyTransactions($revision, $xactions);
|
|
|
|
} catch (PhabricatorApplicationTransactionNoEffectException $ex) {
|
|
|
|
// NOTE: We've marked transactions other than the CLOSE transaction
|
|
|
|
// as ignored when they don't have an effect, so this means that we
|
|
|
|
// lost a race to close the revision. That's perfectly fine, we can
|
|
|
|
// just continue normally.
|
|
|
|
}
|
|
|
|
|
|
|
|
return $result_data;
|
|
|
|
}
|
|
|
|
|
2016-01-08 13:35:17 +01:00
|
|
|
}
|