[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/differential/storage/ -> DifferentialDiff.php (source)

   1  <?php
   2  
   3  final class DifferentialDiff
   4    extends DifferentialDAO
   5    implements
   6      PhabricatorPolicyInterface,
   7      HarbormasterBuildableInterface,
   8      PhabricatorApplicationTransactionInterface,
   9      PhabricatorDestructibleInterface {
  10  
  11    protected $revisionID;
  12    protected $authorPHID;
  13    protected $repositoryPHID;
  14  
  15    protected $sourceMachine;
  16    protected $sourcePath;
  17  
  18    protected $sourceControlSystem;
  19    protected $sourceControlBaseRevision;
  20    protected $sourceControlPath;
  21  
  22    protected $lintStatus;
  23    protected $unitStatus;
  24  
  25    protected $lineCount;
  26  
  27    protected $branch;
  28    protected $bookmark;
  29  
  30    protected $arcanistProjectPHID;
  31    protected $creationMethod;
  32    protected $repositoryUUID;
  33  
  34    protected $description;
  35  
  36    protected $viewPolicy;
  37  
  38    private $unsavedChangesets = array();
  39    private $changesets = self::ATTACHABLE;
  40    private $arcanistProject = self::ATTACHABLE;
  41    private $revision = self::ATTACHABLE;
  42    private $properties = array();
  43  
  44    public function getConfiguration() {
  45      return array(
  46        self::CONFIG_AUX_PHID => true,
  47        self::CONFIG_COLUMN_SCHEMA => array(
  48          'revisionID' => 'id?',
  49          'authorPHID' => 'phid?',
  50          'repositoryPHID' => 'phid?',
  51          'sourceMachine' => 'text255?',
  52          'sourcePath' => 'text255?',
  53          'sourceControlSystem' => 'text64?',
  54          'sourceControlBaseRevision' => 'text255?',
  55          'sourceControlPath' => 'text255?',
  56          'lintStatus' => 'uint32',
  57          'unitStatus' => 'uint32',
  58          'lineCount' => 'uint32',
  59          'branch' => 'text255?',
  60          'bookmark' => 'text255?',
  61          'arcanistProjectPHID' => 'phid?',
  62          'repositoryUUID' => 'text64?',
  63  
  64          // T6203/NULLABILITY
  65          // These should be non-null; all diffs should have a creation method
  66          // and the description should just be empty.
  67          'creationMethod' => 'text255?',
  68          'description' => 'text255?',
  69        ),
  70        self::CONFIG_KEY_SCHEMA => array(
  71          'revisionID' => array(
  72            'columns' => array('revisionID'),
  73          ),
  74        ),
  75      ) + parent::getConfiguration();
  76    }
  77  
  78    public function generatePHID() {
  79      return PhabricatorPHID::generateNewPHID(
  80        DifferentialDiffPHIDType::TYPECONST);
  81    }
  82  
  83    public function addUnsavedChangeset(DifferentialChangeset $changeset) {
  84      if ($this->changesets === null) {
  85        $this->changesets = array();
  86      }
  87      $this->unsavedChangesets[] = $changeset;
  88      $this->changesets[] = $changeset;
  89      return $this;
  90    }
  91  
  92    public function attachChangesets(array $changesets) {
  93      assert_instances_of($changesets, 'DifferentialChangeset');
  94      $this->changesets = $changesets;
  95      return $this;
  96    }
  97  
  98    public function getChangesets() {
  99      return $this->assertAttached($this->changesets);
 100    }
 101  
 102    public function loadChangesets() {
 103      if (!$this->getID()) {
 104        return array();
 105      }
 106      return id(new DifferentialChangeset())->loadAllWhere(
 107        'diffID = %d',
 108        $this->getID());
 109    }
 110  
 111    public function attachArcanistProject(
 112      PhabricatorRepositoryArcanistProject $project = null) {
 113      $this->arcanistProject = $project;
 114      return $this;
 115    }
 116  
 117    public function getArcanistProject() {
 118      return $this->assertAttached($this->arcanistProject);
 119    }
 120  
 121    public function getArcanistProjectName() {
 122      $name = '';
 123      if ($this->arcanistProject) {
 124        $project = $this->getArcanistProject();
 125        $name = $project->getName();
 126      }
 127      return $name;
 128    }
 129  
 130    public function save() {
 131      $this->openTransaction();
 132        $ret = parent::save();
 133        foreach ($this->unsavedChangesets as $changeset) {
 134          $changeset->setDiffID($this->getID());
 135          $changeset->save();
 136        }
 137      $this->saveTransaction();
 138      return $ret;
 139    }
 140  
 141    public static function initializeNewDiff(PhabricatorUser $actor) {
 142      $app = id(new PhabricatorApplicationQuery())
 143        ->setViewer($actor)
 144        ->withClasses(array('PhabricatorDifferentialApplication'))
 145        ->executeOne();
 146      $view_policy = $app->getPolicy(
 147        DifferentialDefaultViewCapability::CAPABILITY);
 148  
 149      $diff = id(new DifferentialDiff())
 150        ->setViewPolicy($view_policy);
 151  
 152      return $diff;
 153    }
 154  
 155    public static function newFromRawChanges(
 156      PhabricatorUser $actor,
 157      array $changes) {
 158  
 159      assert_instances_of($changes, 'ArcanistDiffChange');
 160  
 161      $diff = self::initializeNewDiff($actor);
 162      // There may not be any changes; initialize the changesets list so that
 163      // we don't throw later when accessing it.
 164      $diff->attachChangesets(array());
 165  
 166      $lines = 0;
 167      foreach ($changes as $change) {
 168        if ($change->getType() == ArcanistDiffChangeType::TYPE_MESSAGE) {
 169          // If a user pastes a diff into Differential which includes a commit
 170          // message (e.g., they ran `git show` to generate it), discard that
 171          // change when constructing a DifferentialDiff.
 172          continue;
 173        }
 174  
 175        $changeset = new DifferentialChangeset();
 176        $add_lines = 0;
 177        $del_lines = 0;
 178        $first_line = PHP_INT_MAX;
 179        $hunks = $change->getHunks();
 180        if ($hunks) {
 181          foreach ($hunks as $hunk) {
 182            $dhunk = new DifferentialHunkModern();
 183            $dhunk->setOldOffset($hunk->getOldOffset());
 184            $dhunk->setOldLen($hunk->getOldLength());
 185            $dhunk->setNewOffset($hunk->getNewOffset());
 186            $dhunk->setNewLen($hunk->getNewLength());
 187            $dhunk->setChanges($hunk->getCorpus());
 188            $changeset->addUnsavedHunk($dhunk);
 189            $add_lines += $hunk->getAddLines();
 190            $del_lines += $hunk->getDelLines();
 191            $added_lines = $hunk->getChangedLines('new');
 192            if ($added_lines) {
 193              $first_line = min($first_line, head_key($added_lines));
 194            }
 195          }
 196          $lines += $add_lines + $del_lines;
 197        } else {
 198          // This happens when you add empty files.
 199          $changeset->attachHunks(array());
 200        }
 201  
 202        $metadata = $change->getAllMetadata();
 203        if ($first_line != PHP_INT_MAX) {
 204          $metadata['line:first'] = $first_line;
 205        }
 206  
 207        $changeset->setOldFile($change->getOldPath());
 208        $changeset->setFilename($change->getCurrentPath());
 209        $changeset->setChangeType($change->getType());
 210  
 211        $changeset->setFileType($change->getFileType());
 212        $changeset->setMetadata($metadata);
 213        $changeset->setOldProperties($change->getOldProperties());
 214        $changeset->setNewProperties($change->getNewProperties());
 215        $changeset->setAwayPaths($change->getAwayPaths());
 216        $changeset->setAddLines($add_lines);
 217        $changeset->setDelLines($del_lines);
 218  
 219        $diff->addUnsavedChangeset($changeset);
 220      }
 221      $diff->setLineCount($lines);
 222  
 223      $parser = new DifferentialChangesetParser();
 224      $changesets = $parser->detectCopiedCode(
 225        $diff->getChangesets(),
 226        $min_width = 30,
 227        $min_lines = 3);
 228      $diff->attachChangesets($changesets);
 229  
 230      return $diff;
 231    }
 232  
 233  
 234    public function getDiffDict() {
 235      $dict = array(
 236        'id' => $this->getID(),
 237        'revisionID' => $this->getRevisionID(),
 238        'dateCreated' => $this->getDateCreated(),
 239        'dateModified' => $this->getDateModified(),
 240        'sourceControlBaseRevision' => $this->getSourceControlBaseRevision(),
 241        'sourceControlPath' => $this->getSourceControlPath(),
 242        'sourceControlSystem' => $this->getSourceControlSystem(),
 243        'branch' => $this->getBranch(),
 244        'bookmark' => $this->getBookmark(),
 245        'creationMethod' => $this->getCreationMethod(),
 246        'description' => $this->getDescription(),
 247        'unitStatus' => $this->getUnitStatus(),
 248        'lintStatus' => $this->getLintStatus(),
 249        'changes' => array(),
 250        'properties' => array(),
 251        'projectName' => $this->getArcanistProjectName(),
 252      );
 253  
 254      $dict['changes'] = $this->buildChangesList();
 255  
 256      $properties = id(new DifferentialDiffProperty())->loadAllWhere(
 257        'diffID = %d',
 258        $this->getID());
 259      foreach ($properties as $property) {
 260        $dict['properties'][$property->getName()] = $property->getData();
 261  
 262        if ($property->getName() == 'local:commits') {
 263          foreach ($property->getData() as $commit) {
 264            $dict['authorName'] = $commit['author'];
 265            $dict['authorEmail'] = idx($commit, 'authorEmail');
 266            break;
 267          }
 268        }
 269      }
 270  
 271      return $dict;
 272    }
 273  
 274    public function buildChangesList() {
 275      $changes = array();
 276      foreach ($this->getChangesets() as $changeset) {
 277        $hunks = array();
 278        foreach ($changeset->getHunks() as $hunk) {
 279          $hunks[] = array(
 280            'oldOffset' => $hunk->getOldOffset(),
 281            'newOffset' => $hunk->getNewOffset(),
 282            'oldLength' => $hunk->getOldLen(),
 283            'newLength' => $hunk->getNewLen(),
 284            'addLines'  => null,
 285            'delLines'  => null,
 286            'isMissingOldNewline' => null,
 287            'isMissingNewNewline' => null,
 288            'corpus'    => $hunk->getChanges(),
 289          );
 290        }
 291        $change = array(
 292          'id'            => $changeset->getID(),
 293          'metadata'      => $changeset->getMetadata(),
 294          'oldPath'       => $changeset->getOldFile(),
 295          'currentPath'   => $changeset->getFilename(),
 296          'awayPaths'     => $changeset->getAwayPaths(),
 297          'oldProperties' => $changeset->getOldProperties(),
 298          'newProperties' => $changeset->getNewProperties(),
 299          'type'          => $changeset->getChangeType(),
 300          'fileType'      => $changeset->getFileType(),
 301          'commitHash'    => null,
 302          'addLines'      => $changeset->getAddLines(),
 303          'delLines'      => $changeset->getDelLines(),
 304          'hunks'         => $hunks,
 305        );
 306        $changes[] = $change;
 307      }
 308      return $changes;
 309    }
 310  
 311    public function hasRevision() {
 312      return $this->revision !== self::ATTACHABLE;
 313    }
 314  
 315    public function getRevision() {
 316      return $this->assertAttached($this->revision);
 317    }
 318  
 319    public function attachRevision(DifferentialRevision $revision = null) {
 320      $this->revision = $revision;
 321      return $this;
 322    }
 323  
 324    public function attachProperty($key, $value) {
 325      $this->properties[$key] = $value;
 326      return $this;
 327    }
 328  
 329    public function getProperty($key) {
 330      return $this->assertAttachedKey($this->properties, $key);
 331    }
 332  
 333  
 334  /* -(  PhabricatorPolicyInterface  )----------------------------------------- */
 335  
 336  
 337    public function getCapabilities() {
 338      return array(
 339        PhabricatorPolicyCapability::CAN_VIEW,
 340      );
 341    }
 342  
 343    public function getPolicy($capability) {
 344      if ($this->hasRevision()) {
 345        return $this->getRevision()->getPolicy($capability);
 346      }
 347  
 348      return $this->viewPolicy;
 349    }
 350  
 351    public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
 352      if ($this->hasRevision()) {
 353        return $this->getRevision()->hasAutomaticCapability($capability, $viewer);
 354      }
 355  
 356      return ($this->getAuthorPHID() == $viewer->getPhid());
 357    }
 358  
 359    public function describeAutomaticCapability($capability) {
 360      if ($this->hasRevision()) {
 361        return pht(
 362          'This diff is attached to a revision, and inherits its policies.');
 363      }
 364      return pht('The author of a diff can see it.');
 365    }
 366  
 367  
 368  
 369  /* -(  HarbormasterBuildableInterface  )------------------------------------- */
 370  
 371  
 372    public function getHarbormasterBuildablePHID() {
 373      return $this->getPHID();
 374    }
 375  
 376    public function getHarbormasterContainerPHID() {
 377      if ($this->getRevisionID()) {
 378        $revision = id(new DifferentialRevision())->load($this->getRevisionID());
 379        if ($revision) {
 380          return $revision->getPHID();
 381        }
 382      }
 383  
 384      return null;
 385    }
 386  
 387    public function getBuildVariables() {
 388      $results = array();
 389  
 390      $results['buildable.diff'] = $this->getID();
 391      $revision = $this->getRevision();
 392      $results['buildable.revision'] = $revision->getID();
 393      $repo = $revision->getRepository();
 394  
 395      if ($repo) {
 396        $results['repository.callsign'] = $repo->getCallsign();
 397        $results['repository.vcs'] = $repo->getVersionControlSystem();
 398        $results['repository.uri'] = $repo->getPublicCloneURI();
 399      }
 400  
 401      return $results;
 402    }
 403  
 404    public function getAvailableBuildVariables() {
 405      return array(
 406        'buildable.diff' =>
 407          pht('The differential diff ID, if applicable.'),
 408        'buildable.revision' =>
 409          pht('The differential revision ID, if applicable.'),
 410        'repository.callsign' =>
 411          pht('The callsign of the repository in Phabricator.'),
 412        'repository.vcs' =>
 413          pht('The version control system, either "svn", "hg" or "git".'),
 414        'repository.uri' =>
 415          pht('The URI to clone or checkout the repository from.'),
 416      );
 417    }
 418  
 419  
 420  /* -(  PhabricatorApplicationTransactionInterface  )------------------------- */
 421  
 422  
 423    public function getApplicationTransactionEditor() {
 424      if (!$this->getRevisionID()) {
 425        return null;
 426      }
 427      return $this->getRevision()->getApplicationTransactionEditor();
 428    }
 429  
 430  
 431    public function getApplicationTransactionObject() {
 432      if (!$this->getRevisionID()) {
 433        return null;
 434      }
 435      return $this->getRevision();
 436    }
 437  
 438    public function getApplicationTransactionTemplate() {
 439      if (!$this->getRevisionID()) {
 440        return null;
 441      }
 442      return $this->getRevision()->getApplicationTransactionTemplate();
 443    }
 444  
 445  
 446  /* -(  PhabricatorDestructibleInterface  )----------------------------------- */
 447  
 448  
 449    public function destroyObjectPermanently(
 450      PhabricatorDestructionEngine $engine) {
 451  
 452      $this->openTransaction();
 453        $this->delete();
 454  
 455        foreach ($this->loadChangesets() as $changeset) {
 456          $changeset->delete();
 457        }
 458  
 459        $properties = id(new DifferentialDiffProperty())->loadAllWhere(
 460          'diffID = %d',
 461          $this->getID());
 462        foreach ($properties as $prop) {
 463          $prop->delete();
 464        }
 465  
 466      $this->saveTransaction();
 467    }
 468  
 469  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1