2011-01-24 20:01:53 +01:00
|
|
|
<?php
|
|
|
|
|
2018-02-10 21:04:42 +01:00
|
|
|
final class DifferentialHunk
|
Support storage of Differential hunk data in Files
Summary:
Ref T12932. For long-lived installs, one of the largest tables tends to be the hunk data table. Although it doesn't grow tremendously fast, it's also well suited to storage in Files instead of the database (infrequent access, relatively large blobs of data, mostly one-at-a-time access), and earlier work anticipated eventually adding support for Files storage.
Make Files storage work, and provide `bin/differential migrate-hunk` to manually test/migrate hunks. This is currently the only way hunks get moved to file storage, but I expect to add a GC step which moves them to File storage after 30 days shortly.
The immediate motivation for this is to relieve storage pressure on db001/db002 so we have more headroom for deploying the Ferret engine and its larger indexes (see also T12819).
Test Plan:
- Used `bin/differential migrate-hunk` to move a hunk to and from file storage, verified it survived intact.
- Downloaded the actual stored file, sanity-checked it. Verified permissions.
- Destroyed a diff with `bin/remove destroy`, saw the hunk and file storage destroyed.
- Verified that going from file -> text destroys the old file properly with `migrate-hunk --trace ...`.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T12932
Differential Revision: https://secure.phabricator.com/D18584
2017-09-11 17:08:19 +02:00
|
|
|
extends DifferentialDAO
|
|
|
|
implements
|
|
|
|
PhabricatorPolicyInterface,
|
|
|
|
PhabricatorDestructibleInterface {
|
2011-01-24 20:01:53 +01:00
|
|
|
|
|
|
|
protected $changesetID;
|
|
|
|
protected $oldOffset;
|
|
|
|
protected $oldLen;
|
|
|
|
protected $newOffset;
|
|
|
|
protected $newLen;
|
2018-02-10 21:04:42 +01:00
|
|
|
protected $dataType;
|
|
|
|
protected $dataEncoding;
|
|
|
|
protected $dataFormat;
|
|
|
|
protected $data;
|
2011-01-24 20:01:53 +01:00
|
|
|
|
2014-04-14 21:06:26 +02:00
|
|
|
private $changeset;
|
2015-05-07 01:43:32 +02:00
|
|
|
private $splitLines;
|
2015-03-24 21:11:37 +01:00
|
|
|
private $structuredLines;
|
|
|
|
private $structuredFiles = array();
|
2014-04-14 21:06:26 +02:00
|
|
|
|
2018-02-10 21:04:42 +01:00
|
|
|
private $rawData;
|
|
|
|
private $forcedEncoding;
|
|
|
|
private $fileData;
|
|
|
|
|
2014-04-14 21:06:20 +02:00
|
|
|
const FLAG_LINES_ADDED = 1;
|
|
|
|
const FLAG_LINES_REMOVED = 2;
|
|
|
|
const FLAG_LINES_STABLE = 4;
|
|
|
|
|
2018-02-10 21:04:42 +01:00
|
|
|
const DATATYPE_TEXT = 'text';
|
|
|
|
const DATATYPE_FILE = 'file';
|
|
|
|
|
|
|
|
const DATAFORMAT_RAW = 'byte';
|
|
|
|
const DATAFORMAT_DEFLATED = 'gzde';
|
|
|
|
|
|
|
|
protected function getConfiguration() {
|
|
|
|
return array(
|
|
|
|
self::CONFIG_BINARY => array(
|
|
|
|
'data' => true,
|
|
|
|
),
|
|
|
|
self::CONFIG_COLUMN_SCHEMA => array(
|
|
|
|
'dataType' => 'bytes4',
|
|
|
|
'dataEncoding' => 'text16?',
|
|
|
|
'dataFormat' => 'bytes4',
|
|
|
|
'oldOffset' => 'uint32',
|
|
|
|
'oldLen' => 'uint32',
|
|
|
|
'newOffset' => 'uint32',
|
|
|
|
'newLen' => 'uint32',
|
|
|
|
),
|
|
|
|
self::CONFIG_KEY_SCHEMA => array(
|
|
|
|
'key_changeset' => array(
|
|
|
|
'columns' => array('changesetID'),
|
|
|
|
),
|
|
|
|
'key_created' => array(
|
|
|
|
'columns' => array('dateCreated'),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
) + parent::getConfiguration();
|
|
|
|
}
|
|
|
|
|
2012-04-28 08:00:30 +02:00
|
|
|
public function getAddedLines() {
|
2012-06-27 19:44:29 +02:00
|
|
|
return $this->makeContent($include = '+');
|
2012-04-28 08:00:30 +02:00
|
|
|
}
|
|
|
|
|
2013-10-04 15:37:32 +02:00
|
|
|
public function getRemovedLines() {
|
|
|
|
return $this->makeContent($include = '-');
|
|
|
|
}
|
|
|
|
|
2011-01-24 20:01:53 +01:00
|
|
|
public function makeNewFile() {
|
2012-06-27 19:44:29 +02:00
|
|
|
return implode('', $this->makeContent($include = ' +'));
|
2011-01-24 20:01:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public function makeOldFile() {
|
2012-06-27 19:44:29 +02:00
|
|
|
return implode('', $this->makeContent($include = ' -'));
|
2011-01-24 20:01:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public function makeChanges() {
|
2012-06-27 19:44:29 +02:00
|
|
|
return implode('', $this->makeContent($include = '-+'));
|
2011-01-24 20:01:53 +01:00
|
|
|
}
|
|
|
|
|
2015-03-24 21:11:37 +01:00
|
|
|
public function getStructuredOldFile() {
|
|
|
|
return $this->getStructuredFile('-');
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getStructuredNewFile() {
|
|
|
|
return $this->getStructuredFile('+');
|
|
|
|
}
|
|
|
|
|
|
|
|
private function getStructuredFile($kind) {
|
|
|
|
if ($kind !== '+' && $kind !== '-') {
|
|
|
|
throw new Exception(
|
|
|
|
pht(
|
|
|
|
'Structured file kind should be "+" or "-", got "%s".',
|
|
|
|
$kind));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isset($this->structuredFiles[$kind])) {
|
|
|
|
if ($kind == '+') {
|
|
|
|
$number = $this->newOffset;
|
|
|
|
} else {
|
|
|
|
$number = $this->oldOffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
$lines = $this->getStructuredLines();
|
|
|
|
|
|
|
|
// NOTE: We keep the "\ No newline at end of file" line if it appears
|
|
|
|
// after a line which is not excluded. For example, if we're constructing
|
|
|
|
// the "+" side of the diff, we want to ignore this one since it's
|
|
|
|
// relevant only to the "-" side of the diff:
|
|
|
|
//
|
|
|
|
// - x
|
|
|
|
// \ No newline at end of file
|
|
|
|
// + x
|
|
|
|
//
|
|
|
|
// ...but we want to keep this one:
|
|
|
|
//
|
|
|
|
// - x
|
|
|
|
// + x
|
|
|
|
// \ No newline at end of file
|
|
|
|
|
|
|
|
$file = array();
|
|
|
|
$keep = true;
|
|
|
|
foreach ($lines as $line) {
|
|
|
|
switch ($line['type']) {
|
|
|
|
case ' ':
|
|
|
|
case $kind:
|
|
|
|
$file[$number++] = $line;
|
|
|
|
$keep = true;
|
|
|
|
break;
|
|
|
|
case '\\':
|
|
|
|
if ($keep) {
|
|
|
|
// Strip the actual newline off the line's text.
|
|
|
|
$text = $file[$number - 1]['text'];
|
|
|
|
$text = rtrim($text, "\r\n");
|
|
|
|
$file[$number - 1]['text'] = $text;
|
|
|
|
|
|
|
|
$file[$number++] = $line;
|
|
|
|
$keep = false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
$keep = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->structuredFiles[$kind] = $file;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->structuredFiles[$kind];
|
|
|
|
}
|
|
|
|
|
2015-05-07 01:43:32 +02:00
|
|
|
public function getSplitLines() {
|
|
|
|
if ($this->splitLines === null) {
|
|
|
|
$this->splitLines = phutil_split_lines($this->getChanges());
|
|
|
|
}
|
|
|
|
return $this->splitLines;
|
|
|
|
}
|
|
|
|
|
2015-05-07 23:09:41 +02:00
|
|
|
public function getStructuredLines() {
|
2015-03-24 21:11:37 +01:00
|
|
|
if ($this->structuredLines === null) {
|
2015-05-07 01:43:32 +02:00
|
|
|
$lines = $this->getSplitLines();
|
2015-03-24 21:11:37 +01:00
|
|
|
|
|
|
|
$structured = array();
|
|
|
|
foreach ($lines as $line) {
|
|
|
|
if (empty($line[0])) {
|
|
|
|
// TODO: Can we just get rid of this?
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$structured[] = array(
|
|
|
|
'type' => $line[0],
|
|
|
|
'text' => substr($line, 1),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->structuredLines = $structured;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->structuredLines;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-14 21:06:20 +02:00
|
|
|
public function getContentWithMask($mask) {
|
|
|
|
$include = array();
|
|
|
|
|
|
|
|
if (($mask & self::FLAG_LINES_ADDED)) {
|
|
|
|
$include[] = '+';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (($mask & self::FLAG_LINES_REMOVED)) {
|
|
|
|
$include[] = '-';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (($mask & self::FLAG_LINES_STABLE)) {
|
|
|
|
$include[] = ' ';
|
|
|
|
}
|
|
|
|
|
|
|
|
$include = implode('', $include);
|
|
|
|
|
|
|
|
return implode('', $this->makeContent($include));
|
|
|
|
}
|
|
|
|
|
2012-06-27 19:44:29 +02:00
|
|
|
final private function makeContent($include) {
|
2015-05-07 01:43:32 +02:00
|
|
|
$lines = $this->getSplitLines();
|
2011-01-24 20:01:53 +01:00
|
|
|
$results = array();
|
2012-03-22 22:13:48 +01:00
|
|
|
|
2015-05-07 01:43:32 +02:00
|
|
|
$include_map = array();
|
|
|
|
for ($ii = 0; $ii < strlen($include); $ii++) {
|
|
|
|
$include_map[$include[$ii]] = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isset($include_map['+'])) {
|
|
|
|
$n = $this->newOffset;
|
|
|
|
} else {
|
|
|
|
$n = $this->oldOffset;
|
|
|
|
}
|
2012-03-22 22:13:48 +01:00
|
|
|
|
|
|
|
$use_next_newline = false;
|
2011-01-24 20:01:53 +01:00
|
|
|
foreach ($lines as $line) {
|
2012-06-27 19:44:29 +02:00
|
|
|
if (!isset($line[0])) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($line[0] == '\\') {
|
|
|
|
if ($use_next_newline) {
|
|
|
|
$results[last_key($results)] = rtrim(end($results), "\n");
|
2012-03-22 22:13:48 +01:00
|
|
|
}
|
2015-05-07 01:43:32 +02:00
|
|
|
} else if (empty($include_map[$line[0]])) {
|
2012-06-27 19:44:29 +02:00
|
|
|
$use_next_newline = false;
|
|
|
|
} else {
|
|
|
|
$use_next_newline = true;
|
2015-05-07 01:43:32 +02:00
|
|
|
$results[$n] = substr($line, 1);
|
2011-01-24 20:01:53 +01:00
|
|
|
}
|
2012-03-22 22:13:48 +01:00
|
|
|
|
2015-05-07 01:43:32 +02:00
|
|
|
if ($line[0] == ' ' || isset($include_map[$line[0]])) {
|
2012-06-27 19:44:29 +02:00
|
|
|
$n++;
|
|
|
|
}
|
2012-03-22 22:13:48 +01:00
|
|
|
}
|
|
|
|
|
2012-06-27 19:44:29 +02:00
|
|
|
return $results;
|
2011-01-24 20:01:53 +01:00
|
|
|
}
|
|
|
|
|
2014-04-14 21:06:26 +02:00
|
|
|
public function getChangeset() {
|
|
|
|
return $this->assertAttached($this->changeset);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function attachChangeset(DifferentialChangeset $changeset) {
|
|
|
|
$this->changeset = $changeset;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-02-10 21:04:42 +01:00
|
|
|
/* -( Storage )------------------------------------------------------------ */
|
|
|
|
|
|
|
|
|
|
|
|
public function setChanges($text) {
|
|
|
|
$this->rawData = $text;
|
|
|
|
|
|
|
|
$this->dataEncoding = $this->detectEncodingForStorage($text);
|
|
|
|
$this->dataType = self::DATATYPE_TEXT;
|
|
|
|
|
|
|
|
list($format, $data) = $this->formatDataForStorage($text);
|
|
|
|
|
|
|
|
$this->dataFormat = $format;
|
|
|
|
$this->data = $data;
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getChanges() {
|
|
|
|
return $this->getUTF8StringFromStorage(
|
|
|
|
$this->getRawData(),
|
|
|
|
nonempty($this->forcedEncoding, $this->getDataEncoding()));
|
|
|
|
}
|
|
|
|
|
|
|
|
public function forceEncoding($encoding) {
|
|
|
|
$this->forcedEncoding = $encoding;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function formatDataForStorage($data) {
|
|
|
|
$deflated = PhabricatorCaches::maybeDeflateData($data);
|
|
|
|
if ($deflated !== null) {
|
|
|
|
return array(self::DATAFORMAT_DEFLATED, $deflated);
|
|
|
|
}
|
|
|
|
|
|
|
|
return array(self::DATAFORMAT_RAW, $data);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function saveAsText() {
|
|
|
|
$old_type = $this->getDataType();
|
|
|
|
$old_data = $this->getData();
|
|
|
|
|
|
|
|
if ($old_type == self::DATATYPE_TEXT) {
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
$raw_data = $this->getRawData();
|
|
|
|
|
|
|
|
$this->setDataType(self::DATATYPE_TEXT);
|
|
|
|
|
|
|
|
list($format, $data) = $this->formatDataForStorage($raw_data);
|
|
|
|
$this->setDataFormat($format);
|
|
|
|
$this->setData($data);
|
|
|
|
|
|
|
|
$result = $this->save();
|
|
|
|
|
|
|
|
$this->destroyData($old_type, $old_data);
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function saveAsFile() {
|
|
|
|
$old_type = $this->getDataType();
|
|
|
|
$old_data = $this->getData();
|
|
|
|
|
|
|
|
if ($old_type == self::DATATYPE_FILE) {
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
$raw_data = $this->getRawData();
|
|
|
|
|
|
|
|
list($format, $data) = $this->formatDataForStorage($raw_data);
|
|
|
|
$this->setDataFormat($format);
|
|
|
|
|
|
|
|
$file = PhabricatorFile::newFromFileData(
|
|
|
|
$data,
|
|
|
|
array(
|
|
|
|
'name' => 'differential-hunk',
|
|
|
|
'mime-type' => 'application/octet-stream',
|
|
|
|
'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
|
|
|
|
));
|
|
|
|
|
|
|
|
$this->setDataType(self::DATATYPE_FILE);
|
|
|
|
$this->setData($file->getPHID());
|
|
|
|
|
|
|
|
// NOTE: Because hunks don't have a PHID and we just load hunk data with
|
|
|
|
// the omnipotent viewer, we do not need to attach the file to anything.
|
|
|
|
|
|
|
|
$result = $this->save();
|
|
|
|
|
|
|
|
$this->destroyData($old_type, $old_data);
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function getRawData() {
|
|
|
|
if ($this->rawData === null) {
|
|
|
|
$type = $this->getDataType();
|
|
|
|
$data = $this->getData();
|
|
|
|
|
|
|
|
switch ($type) {
|
|
|
|
case self::DATATYPE_TEXT:
|
|
|
|
// In this storage type, the changes are stored on the object.
|
|
|
|
$data = $data;
|
|
|
|
break;
|
|
|
|
case self::DATATYPE_FILE:
|
|
|
|
$data = $this->loadFileData();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new Exception(
|
|
|
|
pht('Hunk has unsupported data type "%s"!', $type));
|
|
|
|
}
|
|
|
|
|
|
|
|
$format = $this->getDataFormat();
|
|
|
|
switch ($format) {
|
|
|
|
case self::DATAFORMAT_RAW:
|
|
|
|
// In this format, the changes are stored as-is.
|
|
|
|
$data = $data;
|
|
|
|
break;
|
|
|
|
case self::DATAFORMAT_DEFLATED:
|
|
|
|
$data = PhabricatorCaches::inflateData($data);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new Exception(
|
|
|
|
pht('Hunk has unsupported data encoding "%s"!', $type));
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->rawData = $data;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->rawData;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function loadFileData() {
|
|
|
|
if ($this->fileData === null) {
|
|
|
|
$type = $this->getDataType();
|
|
|
|
if ($type !== self::DATATYPE_FILE) {
|
|
|
|
throw new Exception(
|
|
|
|
pht(
|
|
|
|
'Unable to load file data for hunk with wrong data type ("%s").',
|
|
|
|
$type));
|
|
|
|
}
|
|
|
|
|
|
|
|
$file_phid = $this->getData();
|
|
|
|
|
|
|
|
$file = $this->loadRawFile($file_phid);
|
|
|
|
$data = $file->loadFileData();
|
|
|
|
|
|
|
|
$this->fileData = $data;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->fileData;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function loadRawFile($file_phid) {
|
|
|
|
$viewer = PhabricatorUser::getOmnipotentUser();
|
|
|
|
|
|
|
|
|
|
|
|
$files = id(new PhabricatorFileQuery())
|
|
|
|
->setViewer($viewer)
|
|
|
|
->withPHIDs(array($file_phid))
|
|
|
|
->execute();
|
|
|
|
if (!$files) {
|
|
|
|
throw new Exception(
|
|
|
|
pht(
|
|
|
|
'Failed to load file ("%s") with hunk data.',
|
|
|
|
$file_phid));
|
|
|
|
}
|
|
|
|
|
|
|
|
$file = head($files);
|
|
|
|
|
|
|
|
return $file;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function destroyData(
|
|
|
|
$type,
|
|
|
|
$data,
|
|
|
|
PhabricatorDestructionEngine $engine = null) {
|
|
|
|
|
|
|
|
if (!$engine) {
|
|
|
|
$engine = new PhabricatorDestructionEngine();
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ($type) {
|
|
|
|
case self::DATATYPE_FILE:
|
|
|
|
$file = $this->loadRawFile($data);
|
|
|
|
$engine->destroyObject($file);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-14 21:06:26 +02:00
|
|
|
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
|
|
|
|
|
|
|
|
|
|
|
public function getCapabilities() {
|
|
|
|
return array(
|
|
|
|
PhabricatorPolicyCapability::CAN_VIEW,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getPolicy($capability) {
|
|
|
|
return $this->getChangeset()->getPolicy($capability);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
|
|
|
return $this->getChangeset()->hasAutomaticCapability($capability, $viewer);
|
|
|
|
}
|
|
|
|
|
Support storage of Differential hunk data in Files
Summary:
Ref T12932. For long-lived installs, one of the largest tables tends to be the hunk data table. Although it doesn't grow tremendously fast, it's also well suited to storage in Files instead of the database (infrequent access, relatively large blobs of data, mostly one-at-a-time access), and earlier work anticipated eventually adding support for Files storage.
Make Files storage work, and provide `bin/differential migrate-hunk` to manually test/migrate hunks. This is currently the only way hunks get moved to file storage, but I expect to add a GC step which moves them to File storage after 30 days shortly.
The immediate motivation for this is to relieve storage pressure on db001/db002 so we have more headroom for deploying the Ferret engine and its larger indexes (see also T12819).
Test Plan:
- Used `bin/differential migrate-hunk` to move a hunk to and from file storage, verified it survived intact.
- Downloaded the actual stored file, sanity-checked it. Verified permissions.
- Destroyed a diff with `bin/remove destroy`, saw the hunk and file storage destroyed.
- Verified that going from file -> text destroys the old file properly with `migrate-hunk --trace ...`.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T12932
Differential Revision: https://secure.phabricator.com/D18584
2017-09-11 17:08:19 +02:00
|
|
|
|
|
|
|
/* -( PhabricatorDestructibleInterface )----------------------------------- */
|
|
|
|
|
|
|
|
|
|
|
|
public function destroyObjectPermanently(
|
|
|
|
PhabricatorDestructionEngine $engine) {
|
2018-02-10 21:04:42 +01:00
|
|
|
|
|
|
|
$type = $this->getDataType();
|
|
|
|
$data = $this->getData();
|
|
|
|
|
|
|
|
$this->destroyData($type, $data, $engine);
|
|
|
|
|
Support storage of Differential hunk data in Files
Summary:
Ref T12932. For long-lived installs, one of the largest tables tends to be the hunk data table. Although it doesn't grow tremendously fast, it's also well suited to storage in Files instead of the database (infrequent access, relatively large blobs of data, mostly one-at-a-time access), and earlier work anticipated eventually adding support for Files storage.
Make Files storage work, and provide `bin/differential migrate-hunk` to manually test/migrate hunks. This is currently the only way hunks get moved to file storage, but I expect to add a GC step which moves them to File storage after 30 days shortly.
The immediate motivation for this is to relieve storage pressure on db001/db002 so we have more headroom for deploying the Ferret engine and its larger indexes (see also T12819).
Test Plan:
- Used `bin/differential migrate-hunk` to move a hunk to and from file storage, verified it survived intact.
- Downloaded the actual stored file, sanity-checked it. Verified permissions.
- Destroyed a diff with `bin/remove destroy`, saw the hunk and file storage destroyed.
- Verified that going from file -> text destroys the old file properly with `migrate-hunk --trace ...`.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T12932
Differential Revision: https://secure.phabricator.com/D18584
2017-09-11 17:08:19 +02:00
|
|
|
$this->delete();
|
|
|
|
}
|
|
|
|
|
2011-01-24 20:01:53 +01:00
|
|
|
}
|