Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/differential/parser/__tests__/DifferentialChangesetParserTestCase.php b/src/applications/differential/parser/__tests__/DifferentialChangesetParserTestCase.php
index d1f8876103..5003515acf 100644
--- a/src/applications/differential/parser/__tests__/DifferentialChangesetParserTestCase.php
+++ b/src/applications/differential/parser/__tests__/DifferentialChangesetParserTestCase.php
@@ -1,36 +1,36 @@
<?php
final class DifferentialChangesetParserTestCase extends PhabricatorTestCase {
public function testDiffChangesets() {
- $hunk = new DifferentialHunkLegacy();
+ $hunk = new DifferentialHunkModern();
$hunk->setChanges("+a\n b\n-c");
$hunk->setNewOffset(1);
$hunk->setNewLen(2);
$left = new DifferentialChangeset();
$left->attachHunks(array($hunk));
$tests = array(
"+a\n b\n-c" => array(array(), array()),
"+a\n x\n-c" => array(array(), array()),
"+aa\n b\n-c" => array(array(1), array(11)),
" b\n-c" => array(array(1), array()),
"+a\n b\n c" => array(array(), array(13)),
"+a\n x\n c" => array(array(), array(13)),
);
foreach ($tests as $changes => $expected) {
- $hunk = new DifferentialHunkLegacy();
+ $hunk = new DifferentialHunkModern();
$hunk->setChanges($changes);
$hunk->setNewOffset(11);
$hunk->setNewLen(3);
$right = new DifferentialChangeset();
$right->attachHunks(array($hunk));
$parser = new DifferentialChangesetParser();
$parser->setOriginals($left, $right);
$this->assertEqual($expected, $parser->diffOriginals(), $changes);
}
}
}
diff --git a/src/applications/differential/parser/__tests__/DifferentialHunkParserTestCase.php b/src/applications/differential/parser/__tests__/DifferentialHunkParserTestCase.php
index 971b356775..9147ea3ad9 100644
--- a/src/applications/differential/parser/__tests__/DifferentialHunkParserTestCase.php
+++ b/src/applications/differential/parser/__tests__/DifferentialHunkParserTestCase.php
@@ -1,289 +1,289 @@
<?php
final class DifferentialHunkParserTestCase extends PhabricatorTestCase {
private function createComment() {
$comment = new DifferentialInlineComment();
return $comment;
}
private function createHunk(
$old_offset,
$old_len,
$new_offset,
$new_len,
$changes) {
- $hunk = id(new DifferentialHunkLegacy())
+ $hunk = id(new DifferentialHunkModern())
->setOldOffset($old_offset)
->setOldLen($old_len)
->setNewOffset($new_offset)
->setNewLen($new_len)
->setChanges($changes);
return $hunk;
}
// Returns a change that consists of a single hunk, starting at line 1.
private function createSingleChange($old_lines, $new_lines, $changes) {
return array(
0 => $this->createHunk(1, $old_lines, 1, $new_lines, $changes),
);
}
private function createHunksFromFile($name) {
$data = Filesystem::readFile(dirname(__FILE__).'/data/'.$name);
$parser = new ArcanistDiffParser();
$changes = $parser->parseDiff($data);
if (count($changes) !== 1) {
throw new Exception("Expected 1 changeset for '{$name}'!");
}
$diff = DifferentialDiff::newFromRawChanges($changes);
return head($diff->getChangesets())->getHunks();
}
public function testOneLineOldComment() {
$parser = new DifferentialHunkParser();
$hunks = $this->createSingleChange(1, 0, "-a");
$context = $parser->makeContextDiff(
$hunks,
0,
1,
0,
0);
$this->assertEqual("@@ -1,1 @@\n-a", $context);
}
public function testOneLineNewComment() {
$parser = new DifferentialHunkParser();
$hunks = $this->createSingleChange(0, 1, "+a");
$context = $parser->makeContextDiff(
$hunks,
1,
1,
0,
0);
$this->assertEqual("@@ +1,1 @@\n+a", $context);
}
public function testCannotFindContext() {
$parser = new DifferentialHunkParser();
$hunks = $this->createSingleChange(0, 1, "+a");
$context = $parser->makeContextDiff(
$hunks,
1,
2,
0,
0);
$this->assertEqual("", $context);
}
public function testOverlapFromStartOfHunk() {
$parser = new DifferentialHunkParser();
$hunks = array(
0 => $this->createHunk(23, 2, 42, 2, " 1\n 2"),
);
$context = $parser->makeContextDiff(
$hunks,
1,
41,
1,
0);
$this->assertEqual("@@ -23,1 +42,1 @@\n 1", $context);
}
public function testOverlapAfterEndOfHunk() {
$parser = new DifferentialHunkParser();
$hunks = array(
0 => $this->createHunk(23, 2, 42, 2, " 1\n 2"),
);
$context = $parser->makeContextDiff(
$hunks,
1,
43,
1,
0);
$this->assertEqual("@@ -24,1 +43,1 @@\n 2", $context);
}
public function testInclusionOfNewFileInOldCommentFromStart() {
$parser = new DifferentialHunkParser();
$hunks = $this->createSingleChange(2, 3,
"+n1\n".
" e1/2\n".
"-o2\n".
"+n3\n");
$context = $parser->makeContextDiff(
$hunks,
0,
1,
1,
0);
$this->assertEqual(
"@@ -1,2 +2,1 @@\n".
" e1/2\n".
"-o2", $context);
}
public function testInclusionOfOldFileInNewCommentFromStart() {
$parser = new DifferentialHunkParser();
$hunks = $this->createSingleChange(2, 2,
"-o1\n".
" e2/1\n".
"-o3\n".
"+n2\n");
$context = $parser->makeContextDiff(
$hunks,
1,
1,
1,
0);
$this->assertEqual(
"@@ -2,1 +1,2 @@\n".
" e2/1\n".
"+n2", $context);
}
public function testNoNewlineAtEndOfFile() {
$parser = new DifferentialHunkParser();
$hunks = $this->createSingleChange(0, 1,
"+a\n".
"\\No newline at end of file");
// Note that this only works with additional context.
$context = $parser->makeContextDiff(
$hunks,
1,
2,
0,
1);
$this->assertEqual(
"@@ +1,1 @@\n".
"+a\n".
"\\No newline at end of file", $context);
}
public function testMultiLineNewComment() {
$parser = new DifferentialHunkParser();
$hunks = $this->createSingleChange(7, 7,
" e1\n".
" e2\n".
"-o3\n".
"-o4\n".
"+n3\n".
" e5/4\n".
" e6/5\n".
"+n6\n".
" e7\n");
$context = $parser->makeContextDiff(
$hunks,
1,
2,
4,
0);
$this->assertEqual(
"@@ -2,5 +2,5 @@\n".
" e2\n".
"-o3\n".
"-o4\n".
"+n3\n".
" e5/4\n".
" e6/5\n".
"+n6", $context);
}
public function testMultiLineOldComment() {
$parser = new DifferentialHunkParser();
$hunks = $this->createSingleChange(7, 7,
" e1\n".
" e2\n".
"-o3\n".
"-o4\n".
"+n3\n".
" e5/4\n".
" e6/5\n".
"+n6\n".
" e7\n");
$context = $parser->makeContextDiff(
$hunks,
0,
2,
4,
0);
$this->assertEqual(
"@@ -2,5 +2,4 @@\n".
" e2\n".
"-o3\n".
"-o4\n".
"+n3\n".
" e5/4\n".
" e6/5", $context);
}
public function testInclusionOfNewFileInOldCommentFromStartWithContext() {
$parser = new DifferentialHunkParser();
$hunks = $this->createSingleChange(2, 3,
"+n1\n".
" e1/2\n".
"-o2\n".
"+n3\n");
$context = $parser->makeContextDiff(
$hunks,
0,
1,
1,
1);
$this->assertEqual(
"@@ -1,2 +1,2 @@\n".
"+n1\n".
" e1/2\n".
"-o2", $context);
}
public function testInclusionOfOldFileInNewCommentFromStartWithContext() {
$parser = new DifferentialHunkParser();
$hunks = $this->createSingleChange(2, 2,
"-o1\n".
" e2/1\n".
"-o3\n".
"+n2\n");
$context = $parser->makeContextDiff(
$hunks,
1,
1,
1,
1);
$this->assertEqual(
"@@ -1,3 +1,2 @@\n".
"-o1\n".
" e2/1\n".
"-o3\n".
"+n2", $context);
}
public function testMissingContext() {
$tests = array(
'missing_context.diff' => array(
4 => true,
),
'missing_context_2.diff' => array(
5 => true,
),
'missing_context_3.diff' => array(
4 => true,
13 => true,
),
);
foreach ($tests as $name => $expect) {
$hunks = $this->createHunksFromFile($name);
$parser = new DifferentialHunkParser();
$actual = $parser->getHunkStartLines($hunks);
$this->assertEqual($expect, $actual, $name);
}
}
}
diff --git a/src/applications/differential/storage/DifferentialChangeset.php b/src/applications/differential/storage/DifferentialChangeset.php
index cffe77d83e..743bf172b1 100644
--- a/src/applications/differential/storage/DifferentialChangeset.php
+++ b/src/applications/differential/storage/DifferentialChangeset.php
@@ -1,204 +1,211 @@
<?php
final class DifferentialChangeset extends DifferentialDAO
implements PhabricatorPolicyInterface {
protected $diffID;
protected $oldFile;
protected $filename;
protected $awayPaths;
protected $changeType;
protected $fileType;
protected $metadata;
protected $oldProperties;
protected $newProperties;
protected $addLines;
protected $delLines;
private $unsavedHunks = array();
private $hunks = self::ATTACHABLE;
private $diff = self::ATTACHABLE;
const TABLE_CACHE = 'differential_changeset_parse_cache';
protected function getConfiguration() {
return array(
self::CONFIG_SERIALIZATION => array(
'metadata' => self::SERIALIZATION_JSON,
'oldProperties' => self::SERIALIZATION_JSON,
'newProperties' => self::SERIALIZATION_JSON,
'awayPaths' => self::SERIALIZATION_JSON,
)) + parent::getConfiguration();
}
public function getAffectedLineCount() {
return $this->getAddLines() + $this->getDelLines();
}
public function attachHunks(array $hunks) {
assert_instances_of($hunks, 'DifferentialHunk');
$this->hunks = $hunks;
return $this;
}
public function getHunks() {
return $this->assertAttached($this->hunks);
}
public function getDisplayFilename() {
$name = $this->getFilename();
if ($this->getFileType() == DifferentialChangeType::FILE_DIRECTORY) {
$name .= '/';
}
return $name;
}
public function addUnsavedHunk(DifferentialHunk $hunk) {
if ($this->hunks === self::ATTACHABLE) {
$this->hunks = array();
}
$this->hunks[] = $hunk;
$this->unsavedHunks[] = $hunk;
return $this;
}
public function save() {
$this->openTransaction();
$ret = parent::save();
foreach ($this->unsavedHunks as $hunk) {
$hunk->setChangesetID($this->getID());
$hunk->save();
}
$this->saveTransaction();
return $ret;
}
public function delete() {
$this->openTransaction();
- $hunks = id(new DifferentialHunkLegacy())->loadAllWhere(
+ $legacy_hunks = id(new DifferentialHunkLegacy())->loadAllWhere(
'changesetID = %d',
$this->getID());
- foreach ($hunks as $hunk) {
- $hunk->delete();
+ foreach ($legacy_hunks as $legacy_hunk) {
+ $legacy_hunk->delete();
+ }
+
+ $modern_hunks = id(new DifferentialHunkModern())->loadAllWhere(
+ 'changesetID = %d',
+ $this->getID());
+ foreach ($modern_hunks as $modern_hunk) {
+ $modern_hunk->delete();
}
$this->unsavedHunks = array();
queryfx(
$this->establishConnection('w'),
'DELETE FROM %T WHERE id = %d',
self::TABLE_CACHE,
$this->getID());
$ret = parent::delete();
$this->saveTransaction();
return $ret;
}
public function getSortKey() {
$sort_key = $this->getFilename();
// Sort files with ".h" in them first, so headers (.h, .hpp) come before
// implementations (.c, .cpp, .cs).
$sort_key = str_replace('.h', '.!h', $sort_key);
return $sort_key;
}
public function makeNewFile() {
$file = mpull($this->getHunks(), 'makeNewFile');
return implode('', $file);
}
public function makeOldFile() {
$file = mpull($this->getHunks(), 'makeOldFile');
return implode('', $file);
}
public function makeChangesWithContext($num_lines = 3) {
$with_context = array();
foreach ($this->getHunks() as $hunk) {
$context = array();
$changes = explode("\n", $hunk->getChanges());
foreach ($changes as $l => $line) {
$type = substr($line, 0, 1);
if ($type == '+' || $type == '-') {
$context += array_fill($l - $num_lines, 2 * $num_lines + 1, true);
}
}
$with_context[] = array_intersect_key($changes, $context);
}
return array_mergev($with_context);
}
public function getAnchorName() {
return substr(md5($this->getFilename()), 0, 8);
}
public function getAbsoluteRepositoryPath(
PhabricatorRepository $repository = null,
DifferentialDiff $diff = null) {
$base = '/';
if ($diff && $diff->getSourceControlPath()) {
$base = id(new PhutilURI($diff->getSourceControlPath()))->getPath();
}
$path = $this->getFilename();
$path = rtrim($base, '/').'/'.ltrim($path, '/');
$svn = PhabricatorRepositoryType::REPOSITORY_TYPE_SVN;
if ($repository && $repository->getVersionControlSystem() == $svn) {
$prefix = $repository->getDetail('remote-uri');
$prefix = id(new PhutilURI($prefix))->getPath();
if (!strncmp($path, $prefix, strlen($prefix))) {
$path = substr($path, strlen($prefix));
}
$path = '/'.ltrim($path, '/');
}
return $path;
}
public function getWhitespaceMatters() {
$config = PhabricatorEnv::getEnvConfig('differential.whitespace-matters');
foreach ($config as $regexp) {
if (preg_match($regexp, $this->getFilename())) {
return true;
}
}
return false;
}
public function attachDiff(DifferentialDiff $diff) {
$this->diff = $diff;
return $this;
}
public function getDiff() {
return $this->assertAttached($this->diff);
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
public function getPolicy($capability) {
return $this->getDiff()->getPolicy($capability);
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return $this->getDiff()->hasAutomaticCapability($capability, $viewer);
}
public function describeAutomaticCapability($capability) {
return null;
}
}
diff --git a/src/applications/differential/storage/DifferentialDiff.php b/src/applications/differential/storage/DifferentialDiff.php
index 7fe40ff04e..720a5d2d4f 100644
--- a/src/applications/differential/storage/DifferentialDiff.php
+++ b/src/applications/differential/storage/DifferentialDiff.php
@@ -1,386 +1,386 @@
<?php
final class DifferentialDiff
extends DifferentialDAO
implements
PhabricatorPolicyInterface,
HarbormasterBuildableInterface,
PhabricatorApplicationTransactionInterface,
PhabricatorDestructableInterface {
protected $revisionID;
protected $authorPHID;
protected $repositoryPHID;
protected $sourceMachine;
protected $sourcePath;
protected $sourceControlSystem;
protected $sourceControlBaseRevision;
protected $sourceControlPath;
protected $lintStatus;
protected $unitStatus;
protected $lineCount;
protected $branch;
protected $bookmark;
protected $arcanistProjectPHID;
protected $creationMethod;
protected $repositoryUUID;
protected $description;
private $unsavedChangesets = array();
private $changesets = self::ATTACHABLE;
private $arcanistProject = self::ATTACHABLE;
private $revision = self::ATTACHABLE;
private $properties = array();
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
DifferentialPHIDTypeDiff::TYPECONST);
}
public function addUnsavedChangeset(DifferentialChangeset $changeset) {
if ($this->changesets === null) {
$this->changesets = array();
}
$this->unsavedChangesets[] = $changeset;
$this->changesets[] = $changeset;
return $this;
}
public function attachChangesets(array $changesets) {
assert_instances_of($changesets, 'DifferentialChangeset');
$this->changesets = $changesets;
return $this;
}
public function getChangesets() {
return $this->assertAttached($this->changesets);
}
public function loadChangesets() {
if (!$this->getID()) {
return array();
}
return id(new DifferentialChangeset())->loadAllWhere(
'diffID = %d',
$this->getID());
}
public function attachArcanistProject(
PhabricatorRepositoryArcanistProject $project = null) {
$this->arcanistProject = $project;
return $this;
}
public function getArcanistProject() {
return $this->assertAttached($this->arcanistProject);
}
public function getArcanistProjectName() {
$name = '';
if ($this->arcanistProject) {
$project = $this->getArcanistProject();
$name = $project->getName();
}
return $name;
}
public function save() {
$this->openTransaction();
$ret = parent::save();
foreach ($this->unsavedChangesets as $changeset) {
$changeset->setDiffID($this->getID());
$changeset->save();
}
$this->saveTransaction();
return $ret;
}
public static function newFromRawChanges(array $changes) {
assert_instances_of($changes, 'ArcanistDiffChange');
$diff = new DifferentialDiff();
// There may not be any changes; initialize the changesets list so that
// we don't throw later when accessing it.
$diff->attachChangesets(array());
$lines = 0;
foreach ($changes as $change) {
if ($change->getType() == ArcanistDiffChangeType::TYPE_MESSAGE) {
// If a user pastes a diff into Differential which includes a commit
// message (e.g., they ran `git show` to generate it), discard that
// change when constructing a DifferentialDiff.
continue;
}
$changeset = new DifferentialChangeset();
$add_lines = 0;
$del_lines = 0;
$first_line = PHP_INT_MAX;
$hunks = $change->getHunks();
if ($hunks) {
foreach ($hunks as $hunk) {
- $dhunk = new DifferentialHunkLegacy();
+ $dhunk = new DifferentialHunkModern();
$dhunk->setOldOffset($hunk->getOldOffset());
$dhunk->setOldLen($hunk->getOldLength());
$dhunk->setNewOffset($hunk->getNewOffset());
$dhunk->setNewLen($hunk->getNewLength());
$dhunk->setChanges($hunk->getCorpus());
$changeset->addUnsavedHunk($dhunk);
$add_lines += $hunk->getAddLines();
$del_lines += $hunk->getDelLines();
$added_lines = $hunk->getChangedLines('new');
if ($added_lines) {
$first_line = min($first_line, head_key($added_lines));
}
}
$lines += $add_lines + $del_lines;
} else {
// This happens when you add empty files.
$changeset->attachHunks(array());
}
$metadata = $change->getAllMetadata();
if ($first_line != PHP_INT_MAX) {
$metadata['line:first'] = $first_line;
}
$changeset->setOldFile($change->getOldPath());
$changeset->setFilename($change->getCurrentPath());
$changeset->setChangeType($change->getType());
$changeset->setFileType($change->getFileType());
$changeset->setMetadata($metadata);
$changeset->setOldProperties($change->getOldProperties());
$changeset->setNewProperties($change->getNewProperties());
$changeset->setAwayPaths($change->getAwayPaths());
$changeset->setAddLines($add_lines);
$changeset->setDelLines($del_lines);
$diff->addUnsavedChangeset($changeset);
}
$diff->setLineCount($lines);
$parser = new DifferentialChangesetParser();
$changesets = $parser->detectCopiedCode(
$diff->getChangesets(),
$min_width = 30,
$min_lines = 3);
$diff->attachChangesets($changesets);
return $diff;
}
public function getDiffDict() {
$dict = array(
'id' => $this->getID(),
'revisionID' => $this->getRevisionID(),
'dateCreated' => $this->getDateCreated(),
'dateModified' => $this->getDateModified(),
'sourceControlBaseRevision' => $this->getSourceControlBaseRevision(),
'sourceControlPath' => $this->getSourceControlPath(),
'sourceControlSystem' => $this->getSourceControlSystem(),
'branch' => $this->getBranch(),
'bookmark' => $this->getBookmark(),
'creationMethod' => $this->getCreationMethod(),
'description' => $this->getDescription(),
'unitStatus' => $this->getUnitStatus(),
'lintStatus' => $this->getLintStatus(),
'changes' => array(),
'properties' => array(),
'projectName' => $this->getArcanistProjectName()
);
$dict['changes'] = $this->buildChangesList();
$properties = id(new DifferentialDiffProperty())->loadAllWhere(
'diffID = %d',
$this->getID());
foreach ($properties as $property) {
$dict['properties'][$property->getName()] = $property->getData();
if ($property->getName() == 'local:commits') {
foreach ($property->getData() as $commit) {
$dict['authorName'] = $commit['author'];
$dict['authorEmail'] = idx($commit, 'authorEmail');
break;
}
}
}
return $dict;
}
public function buildChangesList() {
$changes = array();
foreach ($this->getChangesets() as $changeset) {
$hunks = array();
foreach ($changeset->getHunks() as $hunk) {
$hunks[] = array(
'oldOffset' => $hunk->getOldOffset(),
'newOffset' => $hunk->getNewOffset(),
'oldLength' => $hunk->getOldLen(),
'newLength' => $hunk->getNewLen(),
'addLines' => null,
'delLines' => null,
'isMissingOldNewline' => null,
'isMissingNewNewline' => null,
'corpus' => $hunk->getChanges(),
);
}
$change = array(
'id' => $changeset->getID(),
'metadata' => $changeset->getMetadata(),
'oldPath' => $changeset->getOldFile(),
'currentPath' => $changeset->getFilename(),
'awayPaths' => $changeset->getAwayPaths(),
'oldProperties' => $changeset->getOldProperties(),
'newProperties' => $changeset->getNewProperties(),
'type' => $changeset->getChangeType(),
'fileType' => $changeset->getFileType(),
'commitHash' => null,
'addLines' => $changeset->getAddLines(),
'delLines' => $changeset->getDelLines(),
'hunks' => $hunks,
);
$changes[] = $change;
}
return $changes;
}
public function getRevision() {
return $this->assertAttached($this->revision);
}
public function attachRevision(DifferentialRevision $revision = null) {
$this->revision = $revision;
return $this;
}
public function attachProperty($key, $value) {
$this->properties[$key] = $value;
return $this;
}
public function getProperty($key) {
return $this->assertAttachedKey($this->properties, $key);
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
public function getPolicy($capability) {
if ($this->getRevision()) {
return $this->getRevision()->getPolicy($capability);
}
return PhabricatorPolicies::POLICY_USER;
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
if ($this->getRevision()) {
return $this->getRevision()->hasAutomaticCapability($capability, $viewer);
}
return false;
}
public function describeAutomaticCapability($capability) {
if ($this->getRevision()) {
return pht(
'This diff is attached to a revision, and inherits its policies.');
}
return null;
}
/* -( HarbormasterBuildableInterface )------------------------------------- */
public function getHarbormasterBuildablePHID() {
return $this->getPHID();
}
public function getHarbormasterContainerPHID() {
if ($this->getRevisionID()) {
$revision = id(new DifferentialRevision())->load($this->getRevisionID());
if ($revision) {
return $revision->getPHID();
}
}
return null;
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
if (!$this->getRevisionID()) {
return null;
}
return $this->getRevision()->getApplicationTransactionEditor();
}
public function getApplicationTransactionObject() {
if (!$this->getRevisionID()) {
return null;
}
return $this->getRevision();
}
public function getApplicationTransactionTemplate() {
if (!$this->getRevisionID()) {
return null;
}
return $this->getRevision()->getApplicationTransactionTemplate();
}
/* -( PhabricatorDestructableInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$this->openTransaction();
$this->delete();
foreach ($this->loadChangesets() as $changeset) {
$changeset->delete();
}
$properties = id(new DifferentialDiffProperty())->loadAllWhere(
'diffID = %d',
$this->getID());
foreach ($properties as $prop) {
$prop->delete();
}
$this->saveTransaction();
}
}
diff --git a/src/applications/differential/storage/__tests__/DifferentialHunkTestCase.php b/src/applications/differential/storage/__tests__/DifferentialHunkTestCase.php
index 6e4d499eb8..785f5e7422 100644
--- a/src/applications/differential/storage/__tests__/DifferentialHunkTestCase.php
+++ b/src/applications/differential/storage/__tests__/DifferentialHunkTestCase.php
@@ -1,37 +1,37 @@
<?php
final class DifferentialHunkTestCase extends ArcanistPhutilTestCase {
public function testMakeChanges() {
$root = dirname(__FILE__).'/hunk/';
- $hunk = new DifferentialHunkLegacy();
+ $hunk = new DifferentialHunkModern();
$hunk->setChanges(Filesystem::readFile($root.'basic.diff'));
$hunk->setOldOffset(1);
$hunk->setNewOffset(11);
$old = Filesystem::readFile($root.'old.txt');
$this->assertEqual($old, $hunk->makeOldFile());
$new = Filesystem::readFile($root.'new.txt');
$this->assertEqual($new, $hunk->makeNewFile());
$added = array(
12 => "1 quack\n",
13 => "1 quack\n",
16 => "5 drake\n",
);
$this->assertEqual($added, $hunk->getAddedLines());
- $hunk = new DifferentialHunkLegacy();
+ $hunk = new DifferentialHunkModern();
$hunk->setChanges(Filesystem::readFile($root.'newline.diff'));
$hunk->setOldOffset(1);
$hunk->setNewOffset(11);
$this->assertEqual("a\n", $hunk->makeOldFile());
$this->assertEqual("a", $hunk->makeNewFile());
$this->assertEqual(array(11 => "a"), $hunk->getAddedLines());
}
}
diff --git a/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php b/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php
index c12bdd5e0e..9b1fa004ed 100644
--- a/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php
+++ b/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php
@@ -1,512 +1,512 @@
<?php
abstract class PhabricatorRepositoryCommitMessageParserWorker
extends PhabricatorRepositoryCommitParserWorker {
final protected function updateCommitData(DiffusionCommitRef $ref) {
$commit = $this->commit;
$author = $ref->getAuthor();
$message = $ref->getMessage();
$committer = $ref->getCommitter();
$hashes = $ref->getHashes();
$data = id(new PhabricatorRepositoryCommitData())->loadOneWhere(
'commitID = %d',
$commit->getID());
if (!$data) {
$data = new PhabricatorRepositoryCommitData();
}
$data->setCommitID($commit->getID());
$data->setAuthorName($author);
$data->setCommitDetail(
'authorPHID',
$this->resolveUserPHID($commit, $author));
$data->setCommitMessage($message);
if (strlen($committer)) {
$data->setCommitDetail('committer', $committer);
$data->setCommitDetail(
'committerPHID',
$this->resolveUserPHID($commit, $committer));
}
$repository = $this->repository;
$author_phid = $data->getCommitDetail('authorPHID');
$committer_phid = $data->getCommitDetail('committerPHID');
$user = new PhabricatorUser();
if ($author_phid) {
$user = $user->loadOneWhere(
'phid = %s',
$author_phid);
}
$field_values = id(new DiffusionLowLevelCommitFieldsQuery())
->setRepository($repository)
->withCommitRef($ref)
->execute();
$revision_id = idx($field_values, 'revisionID');
if (!empty($field_values['reviewedByPHIDs'])) {
$data->setCommitDetail(
'reviewerPHID',
reset($field_values['reviewedByPHIDs']));
}
$data->setCommitDetail('differential.revisionID', $revision_id);
if ($author_phid != $commit->getAuthorPHID()) {
$commit->setAuthorPHID($author_phid);
}
$commit->setSummary($data->getSummary());
$commit->save();
$conn_w = id(new DifferentialRevision())->establishConnection('w');
// NOTE: The `differential_commit` table has a unique ID on `commitPHID`,
// preventing more than one revision from being associated with a commit.
// Generally this is good and desirable, but with the advent of hash
// tracking we may end up in a situation where we match several different
// revisions. We just kind of ignore this and pick one, we might want to
// revisit this and do something differently. (If we match several revisions
// someone probably did something very silly, though.)
$revision = null;
$should_autoclose = $repository->shouldAutocloseCommit($commit, $data);
if ($revision_id) {
// TODO: Check if a more restrictive viewer could be set here
$revision_query = id(new DifferentialRevisionQuery())
->withIDs(array($revision_id))
->setViewer(PhabricatorUser::getOmnipotentUser())
->needReviewerStatus(true)
->needActiveDiffs(true);
$revision = $revision_query->executeOne();
if ($revision) {
$commit_drev = PhabricatorEdgeConfig::TYPE_COMMIT_HAS_DREV;
id(new PhabricatorEdgeEditor())
->setActor($user)
->addEdge($commit->getPHID(), $commit_drev, $revision->getPHID())
->save();
queryfx(
$conn_w,
'INSERT IGNORE INTO %T (revisionID, commitPHID) VALUES (%d, %s)',
DifferentialRevision::TABLE_COMMIT,
$revision->getID(),
$commit->getPHID());
$status_closed = ArcanistDifferentialRevisionStatus::CLOSED;
$should_close = ($revision->getStatus() != $status_closed) &&
$should_autoclose;
if ($should_close) {
$actor_phid = nonempty(
$committer_phid,
$author_phid,
$revision->getAuthorPHID());
$actor = id(new PhabricatorUser())
->loadOneWhere('phid = %s', $actor_phid);
$commit_name = $repository->formatCommitName(
$commit->getCommitIdentifier());
$committer_name = $this->loadUserName(
$committer_phid,
$data->getCommitDetail('committer'),
$actor);
$author_name = $this->loadUserName(
$author_phid,
$data->getAuthorName(),
$actor);
if ($committer_name && ($committer_name != $author_name)) {
$revision_update_comment = pht(
'Closed by commit %s (authored by %s, committed by %s).',
$commit_name,
$author_name,
$committer_name);
} else {
$revision_update_comment = pht(
'Closed by commit %s (authored by %s).',
$commit_name,
$author_name);
}
$diff = $this->generateFinalDiff($revision, $actor_phid);
$vs_diff = $this->loadChangedByCommit($revision, $diff);
$changed_uri = null;
if ($vs_diff) {
$data->setCommitDetail('vsDiff', $vs_diff->getID());
$changed_uri = PhabricatorEnv::getProductionURI(
'/D'.$revision->getID().
'?vs='.$vs_diff->getID().
'&id='.$diff->getID().
'#toc');
}
$xactions = array();
$xactions[] = id(new DifferentialTransaction())
->setTransactionType(DifferentialTransaction::TYPE_ACTION)
->setNewValue(DifferentialAction::ACTION_CLOSE);
$xactions[] = id(new DifferentialTransaction())
->setTransactionType(DifferentialTransaction::TYPE_UPDATE)
->setIgnoreOnNoEffect(true)
->setNewValue($diff->getPHID());
$xactions[] = id(new DifferentialTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
->setIgnoreOnNoEffect(true)
->attachComment(
id(new DifferentialTransactionComment())
->setContent($revision_update_comment));
$content_source = PhabricatorContentSource::newForSource(
PhabricatorContentSource::SOURCE_DAEMON,
array());
$editor = id(new DifferentialTransactionEditor())
->setActor($actor)
->setContinueOnMissingFields(true)
->setContentSource($content_source)
->setChangedPriorToCommitURI($changed_uri)
->setIsCloseByCommit(true);
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.
}
}
}
}
if ($should_autoclose) {
// TODO: This isn't as general as it could be.
if ($user->getPHID()) {
$this->closeTasks($user, $repository, $commit, $message);
}
}
$data->save();
$commit->writeImportStatusFlag(
PhabricatorRepositoryCommit::IMPORTED_MESSAGE);
}
private function loadUserName($user_phid, $default, PhabricatorUser $actor) {
if (!$user_phid) {
return $default;
}
$handle = id(new PhabricatorHandleQuery())
->setViewer($actor)
->withPHIDs(array($user_phid))
->executeOne();
return '@'.$handle->getName();
}
private function generateFinalDiff(
DifferentialRevision $revision,
$actor_phid) {
$viewer = PhabricatorUser::getOmnipotentUser();
$drequest = DiffusionRequest::newFromDictionary(array(
'user' => $viewer,
'repository' => $this->repository,
));
$raw_diff = DiffusionQuery::callConduitWithDiffusionRequest(
$viewer,
$drequest,
'diffusion.rawdiffquery',
array(
'commit' => $this->commit->getCommitIdentifier(),
));
// 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($changes)
->setRepositoryPHID($this->repository->getPHID())
->setAuthorPHID($actor_phid)
->setCreationMethod('commit')
->setSourceControlSystem($this->repository->getVersionControlSystem())
->setLintStatus(DifferentialLintStatus::LINT_SKIP)
->setUnitStatus(DifferentialUnitStatus::UNIT_SKIP)
->setDateCreated($this->commit->getEpoch())
->setDescription(
'Commit r'.
$this->repository->getCallsign().
$this->commit->getCommitIdentifier());
// TODO: This is not correct in SVN where one repository can have multiple
// Arcanist projects.
$arcanist_project = id(new PhabricatorRepositoryArcanistProject())
->loadOneWhere('repositoryID = %d LIMIT 1', $this->repository->getID());
if ($arcanist_project) {
$diff->setArcanistProjectPHID($arcanist_project->getPHID());
}
$parents = DiffusionQuery::callConduitWithDiffusionRequest(
$viewer,
$drequest,
'diffusion.commitparentsquery',
array(
'commit' => $this->commit->getCommitIdentifier(),
));
if ($parents) {
$diff->setSourceControlBaseRevision(head($parents));
}
// TODO: Attach binary files.
return $diff->save();
}
private function loadChangedByCommit(
DifferentialRevision $revision,
DifferentialDiff $diff) {
$repository = $this->repository;
$vs_diff = id(new DifferentialDiffQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withRevisionIDs(array($revision->getID()))
->needChangesets(true)
->setLimit(1)
->executeOne();
if (!$vs_diff) {
return null;
}
if ($vs_diff->getCreationMethod() == 'commit') {
return null;
}
$vs_changesets = array();
foreach ($vs_diff->getChangesets() as $changeset) {
$path = $changeset->getAbsoluteRepositoryPath($repository, $vs_diff);
$path = ltrim($path, '/');
$vs_changesets[$path] = $changeset;
}
$changesets = array();
foreach ($diff->getChangesets() as $changeset) {
$path = $changeset->getAbsoluteRepositoryPath($repository, $diff);
$path = ltrim($path, '/');
$changesets[$path] = $changeset;
}
if (array_fill_keys(array_keys($changesets), true) !=
array_fill_keys(array_keys($vs_changesets), true)) {
return $vs_diff;
}
$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 $vs_diff;
}
$drequest = DiffusionRequest::newFromDictionary(array(
'user' => PhabricatorUser::getOmnipotentUser(),
'initFromConduit' => false,
'repository' => $this->repository,
'commit' => $this->commit->getCommitIdentifier(),
'path' => $path,
));
$corpus = DiffusionFileContentQuery::newFromDiffusionRequest($drequest)
->setViewer(PhabricatorUser::getOmnipotentUser())
->loadFileContent()
->getCorpus();
if ($files[$file_phid]->loadFileData() != $corpus) {
return $vs_diff;
}
} 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 DifferentialHunkLegacy())->setChanges($context);
- $vs_hunk = id(new DifferentialHunkLegacy())->setChanges($vs_context);
+ $hunk = id(new DifferentialHunkModern())->setChanges($context);
+ $vs_hunk = id(new DifferentialHunkModern())->setChanges($vs_context);
if ($hunk->makeOldFile() != $vs_hunk->makeOldFile() ||
$hunk->makeNewFile() != $vs_hunk->makeNewFile()) {
return $vs_diff;
}
}
}
return null;
}
private function resolveUserPHID(
PhabricatorRepositoryCommit $commit,
$user_name) {
return id(new DiffusionResolveUserQuery())
->withCommit($commit)
->withName($user_name)
->execute();
}
private function closeTasks(
PhabricatorUser $actor,
PhabricatorRepository $repository,
PhabricatorRepositoryCommit $commit,
$message) {
$maniphest = 'PhabricatorApplicationManiphest';
if (!PhabricatorApplication::isClassInstalled($maniphest)) {
return;
}
$prefixes = ManiphestTaskStatus::getStatusPrefixMap();
$suffixes = ManiphestTaskStatus::getStatusSuffixMap();
$matches = id(new ManiphestCustomFieldStatusParser())
->parseCorpus($message);
$task_statuses = array();
foreach ($matches as $match) {
$prefix = phutil_utf8_strtolower($match['prefix']);
$suffix = phutil_utf8_strtolower($match['suffix']);
$status = idx($suffixes, $suffix);
if (!$status) {
$status = idx($prefixes, $prefix);
}
foreach ($match['monograms'] as $task_monogram) {
$task_id = (int)trim($task_monogram, 'tT');
$task_statuses[$task_id] = $status;
}
}
if (!$task_statuses) {
return;
}
$tasks = id(new ManiphestTaskQuery())
->setViewer($actor)
->withIDs(array_keys($task_statuses))
->execute();
foreach ($tasks as $task_id => $task) {
$xactions = array();
// TODO: Swap this for a real edge transaction once the weirdness in
// Maniphest edges is sorted out. Currently, Maniphest reacts to an edge
// edit on this edge.
id(new PhabricatorEdgeEditor())
->setActor($actor)
->addEdge(
$task->getPHID(),
PhabricatorEdgeConfig::TYPE_TASK_HAS_COMMIT,
$commit->getPHID())
->save();
/* TODO: Do this instead of the above.
$xactions[] = id(new ManiphestTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $edge_task_has_commit)
->setNewValue(
array(
'+' => array(
$commit->getPHID() => $commit->getPHID(),
),
));
*/
$status = $task_statuses[$task_id];
if ($status) {
if ($task->getStatus() != $status) {
$xactions[] = id(new ManiphestTransaction())
->setTransactionType(ManiphestTransaction::TYPE_STATUS)
->setNewValue($status);
$commit_name = $repository->formatCommitName(
$commit->getCommitIdentifier());
$status_message = pht(
'Closed by commit %s.',
$commit_name);
$xactions[] = id(new ManiphestTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
->attachComment(
id(new ManiphestTransactionComment())
->setContent($status_message));
}
}
$content_source = PhabricatorContentSource::newForSource(
PhabricatorContentSource::SOURCE_DAEMON,
array());
$editor = id(new ManiphestTransactionEditor())
->setActor($actor)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true)
->setContentSource($content_source);
$editor->applyTransactions($task, $xactions);
}
}
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jul 29, 2:09 AM (2 w, 1 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
187860
Default Alt Text
(45 KB)

Event Timeline